root/chrome/browser/chromeos/options/vpn_config_view.cc

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. ProviderTypeIndexToString
  2. ProviderTypeToIndex
  3. ProviderTypeIndexToONCDictKey
  4. GetPemFromDictionary
  5. GetItemCount
  6. GetItemAt
  7. GetItemCount
  8. GetItemAt
  9. GetItemCount
  10. GetItemAt
  11. weak_ptr_factory_
  12. GetTitle
  13. GetInitiallyFocusedView
  14. CanLogin
  15. ContentsChanged
  16. HandleKeyEvent
  17. ButtonPressed
  18. OnPerformAction
  19. OnCertificatesLoaded
  20. Login
  21. Cancel
  22. InitFocus
  23. GetService
  24. GetServer
  25. GetPSKPassphrase
  26. GetUsername
  27. GetUserPassphrase
  28. GetGroupName
  29. GetOTP
  30. GetServerCACertPEM
  31. GetUserCertID
  32. GetSaveCredentials
  33. GetProviderTypeIndex
  34. GetProviderTypeString
  35. Init
  36. InitFromProperties
  37. ParseUIProperties
  38. GetPropertiesError
  39. SetConfigProperties
  40. Refresh
  41. UpdateControlsToEnable
  42. UpdateControls
  43. UpdateErrorLabel
  44. UpdateCanLogin
  45. HaveUserCerts
  46. IsUserCertValid
  47. GetTextFromField
  48. GetPassphraseFromField
  49. ParseVPNUIProperty

// Copyright (c) 2012 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/options/vpn_config_view.h"

#include "ash/system/chromeos/network/network_connect.h"
#include "base/bind.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "chrome/browser/chromeos/enrollment_dialog_view.h"
#include "chrome/browser/chromeos/net/onc_utils.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/common/net/x509_certificate_model.h"
#include "chromeos/login/login_state.h"
#include "chromeos/network/network_configuration_handler.h"
#include "chromeos/network/network_event_log.h"
#include "chromeos/network/network_state.h"
#include "chromeos/network/network_state_handler.h"
#include "chromeos/network/network_ui_data.h"
#include "components/onc/onc_constants.h"
#include "grit/chromium_strings.h"
#include "grit/generated_resources.h"
#include "grit/locale_settings.h"
#include "grit/theme_resources.h"
#include "third_party/cros_system_api/dbus/service_constants.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/models/combobox_model.h"
#include "ui/base/resource/resource_bundle.h"
#include "ui/events/event.h"
#include "ui/views/controls/button/checkbox.h"
#include "ui/views/controls/combobox/combobox.h"
#include "ui/views/controls/label.h"
#include "ui/views/controls/textfield/textfield.h"
#include "ui/views/layout/grid_layout.h"
#include "ui/views/layout/layout_constants.h"
#include "ui/views/widget/widget.h"
#include "ui/views/window/dialog_client_view.h"

namespace {

enum ProviderTypeIndex {
  PROVIDER_TYPE_INDEX_L2TP_IPSEC_PSK = 0,
  PROVIDER_TYPE_INDEX_L2TP_IPSEC_USER_CERT = 1,
  PROVIDER_TYPE_INDEX_OPEN_VPN = 2,
  PROVIDER_TYPE_INDEX_MAX = 3,
};

base::string16 ProviderTypeIndexToString(int index) {
  switch (index) {
    case PROVIDER_TYPE_INDEX_L2TP_IPSEC_PSK:
      return l10n_util::GetStringUTF16(
          IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_L2TP_IPSEC_PSK);
    case PROVIDER_TYPE_INDEX_L2TP_IPSEC_USER_CERT:
      return l10n_util::GetStringUTF16(
          IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_L2TP_IPSEC_USER_CERT);
    case PROVIDER_TYPE_INDEX_OPEN_VPN:
      return l10n_util::GetStringUTF16(
          IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_OPEN_VPN);
  }
  NOTREACHED();
  return base::string16();
}

int ProviderTypeToIndex(const std::string& provider_type,
                        const std::string& client_cert_id) {
  if (provider_type == shill::kProviderL2tpIpsec) {
    if (!client_cert_id.empty())
      return PROVIDER_TYPE_INDEX_L2TP_IPSEC_USER_CERT;
    else
      return PROVIDER_TYPE_INDEX_L2TP_IPSEC_PSK;
  } else {
    DCHECK(provider_type == shill::kProviderOpenVpn);
    return PROVIDER_TYPE_INDEX_OPEN_VPN;
  }
}

// Translates the provider type to the name of the respective ONC dictionary
// containing configuration data for the type.
std::string ProviderTypeIndexToONCDictKey(int provider_type_index) {
  switch (provider_type_index) {
    case PROVIDER_TYPE_INDEX_L2TP_IPSEC_PSK:
    case PROVIDER_TYPE_INDEX_L2TP_IPSEC_USER_CERT:
      return onc::vpn::kIPsec;
    case PROVIDER_TYPE_INDEX_OPEN_VPN:
      return onc::vpn::kOpenVPN;
  }
  NOTREACHED() << "Unhandled provider type index " << provider_type_index;
  return std::string();
}

std::string GetPemFromDictionary(
    const base::DictionaryValue* provider_properties,
    const std::string& key) {
  const base::ListValue* pems = NULL;
  if (!provider_properties->GetListWithoutPathExpansion(key, &pems))
    return std::string();
  std::string pem;
  pems->GetString(0, &pem);
  return pem;
}

}  // namespace

namespace chromeos {

namespace internal {

class ProviderTypeComboboxModel : public ui::ComboboxModel {
 public:
  ProviderTypeComboboxModel();
  virtual ~ProviderTypeComboboxModel();

  // Overridden from ui::ComboboxModel:
  virtual int GetItemCount() const OVERRIDE;
  virtual base::string16 GetItemAt(int index) OVERRIDE;

 private:
  DISALLOW_COPY_AND_ASSIGN(ProviderTypeComboboxModel);
};

class VpnServerCACertComboboxModel : public ui::ComboboxModel {
 public:
  VpnServerCACertComboboxModel();
  virtual ~VpnServerCACertComboboxModel();

  // Overridden from ui::ComboboxModel:
  virtual int GetItemCount() const OVERRIDE;
  virtual base::string16 GetItemAt(int index) OVERRIDE;

 private:
  DISALLOW_COPY_AND_ASSIGN(VpnServerCACertComboboxModel);
};

class VpnUserCertComboboxModel : public ui::ComboboxModel {
 public:
  VpnUserCertComboboxModel();
  virtual ~VpnUserCertComboboxModel();

  // Overridden from ui::ComboboxModel:
  virtual int GetItemCount() const OVERRIDE;
  virtual base::string16 GetItemAt(int index) OVERRIDE;

 private:
  DISALLOW_COPY_AND_ASSIGN(VpnUserCertComboboxModel);
};

// ProviderTypeComboboxModel ---------------------------------------------------

ProviderTypeComboboxModel::ProviderTypeComboboxModel() {
}

ProviderTypeComboboxModel::~ProviderTypeComboboxModel() {
}

int ProviderTypeComboboxModel::GetItemCount() const {
  return PROVIDER_TYPE_INDEX_MAX;
}

base::string16 ProviderTypeComboboxModel::GetItemAt(int index) {
  return ProviderTypeIndexToString(index);
}

// VpnServerCACertComboboxModel ------------------------------------------------

VpnServerCACertComboboxModel::VpnServerCACertComboboxModel() {
}

VpnServerCACertComboboxModel::~VpnServerCACertComboboxModel() {
}

int VpnServerCACertComboboxModel::GetItemCount() const {
  if (CertLibrary::Get()->CertificatesLoading())
    return 1;  // "Loading"
  // "Default" + certs.
  return CertLibrary::Get()->NumCertificates(
      CertLibrary::CERT_TYPE_SERVER_CA) + 1;
}

base::string16 VpnServerCACertComboboxModel::GetItemAt(int index) {
  if (CertLibrary::Get()->CertificatesLoading())
    return l10n_util::GetStringUTF16(
        IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_CERT_LOADING);
  if (index == 0)
    return l10n_util::GetStringUTF16(
        IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_CERT_SERVER_CA_DEFAULT);
  int cert_index = index - 1;
  return CertLibrary::Get()->GetCertDisplayStringAt(
      CertLibrary::CERT_TYPE_SERVER_CA, cert_index);
}

// VpnUserCertComboboxModel ----------------------------------------------------

VpnUserCertComboboxModel::VpnUserCertComboboxModel() {
}

VpnUserCertComboboxModel::~VpnUserCertComboboxModel() {
}

int VpnUserCertComboboxModel::GetItemCount() const {
  if (CertLibrary::Get()->CertificatesLoading())
    return 1;  // "Loading"
  int num_certs =
      CertLibrary::Get()->NumCertificates(CertLibrary::CERT_TYPE_USER);
  if (num_certs == 0)
    return 1;  // "None installed"
  return num_certs;
}

base::string16 VpnUserCertComboboxModel::GetItemAt(int index) {
  if (CertLibrary::Get()->CertificatesLoading()) {
    return l10n_util::GetStringUTF16(
        IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_CERT_LOADING);
  }
  if (CertLibrary::Get()->NumCertificates(CertLibrary::CERT_TYPE_USER) == 0) {
    return l10n_util::GetStringUTF16(
        IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_USER_CERT_NONE_INSTALLED);
  }
  return CertLibrary::Get()->GetCertDisplayStringAt(
      CertLibrary::CERT_TYPE_USER, index);
}

}  // namespace internal

VPNConfigView::VPNConfigView(NetworkConfigView* parent,
                             const std::string& service_path)
    : ChildNetworkConfigView(parent, service_path),
      service_text_modified_(false),
      enable_psk_passphrase_(false),
      enable_user_cert_(false),
      enable_server_ca_cert_(false),
      enable_otp_(false),
      enable_group_name_(false),
      title_(0),
      layout_(NULL),
      server_textfield_(NULL),
      service_text_(NULL),
      service_textfield_(NULL),
      provider_type_combobox_(NULL),
      provider_type_text_label_(NULL),
      psk_passphrase_label_(NULL),
      psk_passphrase_textfield_(NULL),
      user_cert_label_(NULL),
      user_cert_combobox_(NULL),
      server_ca_cert_label_(NULL),
      server_ca_cert_combobox_(NULL),
      username_textfield_(NULL),
      user_passphrase_textfield_(NULL),
      otp_label_(NULL),
      otp_textfield_(NULL),
      group_name_label_(NULL),
      group_name_textfield_(NULL),
      save_credentials_checkbox_(NULL),
      error_label_(NULL),
      provider_type_index_(PROVIDER_TYPE_INDEX_MAX),
      weak_ptr_factory_(this) {
  Init();
}

VPNConfigView::~VPNConfigView() {
  RemoveAllChildViews(true);  // Destroy children before models
  CertLibrary::Get()->RemoveObserver(this);
}

base::string16 VPNConfigView::GetTitle() const {
  DCHECK_NE(title_, 0);
  return l10n_util::GetStringUTF16(title_);
}

views::View* VPNConfigView::GetInitiallyFocusedView() {
  if (service_path_.empty()) {
    // Put focus in the first editable field.
    if (server_textfield_)
      return server_textfield_;
    else if (service_textfield_)
      return service_textfield_;
    else if (provider_type_combobox_)
      return provider_type_combobox_;
    else if (psk_passphrase_textfield_ && psk_passphrase_textfield_->enabled())
      return psk_passphrase_textfield_;
    else if (user_cert_combobox_ && user_cert_combobox_->enabled())
      return user_cert_combobox_;
    else if (server_ca_cert_combobox_ && server_ca_cert_combobox_->enabled())
      return server_ca_cert_combobox_;
  }
  if (user_passphrase_textfield_)
    return user_passphrase_textfield_;
  else if (otp_textfield_)
    return otp_textfield_;
  return NULL;
}

bool VPNConfigView::CanLogin() {
  // Username is always required.
  if (GetUsername().empty())
    return false;

  // TODO(stevenjb): min kMinPassphraseLen length?
  if (service_path_.empty() &&
      (GetService().empty() || GetServer().empty()))
    return false;

  // Block login if certs are required but user has none.
  bool cert_required =
      GetProviderTypeIndex() == PROVIDER_TYPE_INDEX_L2TP_IPSEC_USER_CERT;
  if (cert_required && (!HaveUserCerts() || !IsUserCertValid()))
    return false;

  return true;
}

void VPNConfigView::ContentsChanged(views::Textfield* sender,
                                    const base::string16& new_contents) {
  if (sender == server_textfield_ && !service_text_modified_) {
    // Set the service name to the server name up to '.', unless it has
    // been explicitly set by the user.
    base::string16 server = server_textfield_->text();
    base::string16::size_type n = server.find_first_of(L'.');
    service_name_from_server_ = server.substr(0, n);
    service_textfield_->SetText(service_name_from_server_);
  }
  if (sender == service_textfield_) {
    if (new_contents.empty())
      service_text_modified_ = false;
    else if (new_contents != service_name_from_server_)
      service_text_modified_ = true;
  }
  UpdateCanLogin();
}

bool VPNConfigView::HandleKeyEvent(views::Textfield* sender,
                                   const ui::KeyEvent& key_event) {
  if ((sender == psk_passphrase_textfield_ ||
       sender == user_passphrase_textfield_) &&
      key_event.key_code() == ui::VKEY_RETURN) {
    parent_->GetDialogClientView()->AcceptWindow();
  }
  return false;
}

void VPNConfigView::ButtonPressed(views::Button* sender,
                                  const ui::Event& event) {
}

void VPNConfigView::OnPerformAction(views::Combobox* combobox) {
  UpdateControls();
  UpdateErrorLabel();
  UpdateCanLogin();
}

void VPNConfigView::OnCertificatesLoaded(bool initial_load) {
  Refresh();
}

bool VPNConfigView::Login() {
  if (service_path_.empty()) {
    base::DictionaryValue properties;
    // Identifying properties
    properties.SetStringWithoutPathExpansion(
        shill::kTypeProperty, shill::kTypeVPN);
    properties.SetStringWithoutPathExpansion(
        shill::kNameProperty, GetService());
    properties.SetStringWithoutPathExpansion(
        shill::kProviderHostProperty, GetServer());
    properties.SetStringWithoutPathExpansion(
        shill::kProviderTypeProperty, GetProviderTypeString());

    SetConfigProperties(&properties);
    bool shared = !LoginState::Get()->IsUserAuthenticated();

    bool only_policy_autoconnect =
        onc::PolicyAllowsOnlyPolicyNetworksToAutoconnect(!shared);
    if (only_policy_autoconnect) {
      properties.SetBooleanWithoutPathExpansion(shill::kAutoConnectProperty,
                                                false);
    }

    ash::network_connect::CreateConfigurationAndConnect(&properties, shared);
  } else {
    const NetworkState* vpn = NetworkHandler::Get()->network_state_handler()->
        GetNetworkState(service_path_);
    if (!vpn) {
      // Shill no longer knows about this network (edge case).
      // TODO(stevenjb): Add notification for this.
      NET_LOG_ERROR("Network not found", service_path_);
      return true;  // Close dialog
    }
    base::DictionaryValue properties;
    SetConfigProperties(&properties);
    ash::network_connect::ConfigureNetworkAndConnect(
        service_path_, properties, false /* not shared */);
  }
  return true;  // Close dialog.
}

void VPNConfigView::Cancel() {
}

void VPNConfigView::InitFocus() {
  views::View* view_to_focus = GetInitiallyFocusedView();
  if (view_to_focus)
    view_to_focus->RequestFocus();
}

const std::string VPNConfigView::GetService() const {
  if (service_textfield_ != NULL)
    return GetTextFromField(service_textfield_, true);
  return service_path_;
}

const std::string VPNConfigView::GetServer() const {
  if (server_textfield_ != NULL)
    return GetTextFromField(server_textfield_, true);
  return std::string();
}

const std::string VPNConfigView::GetPSKPassphrase() const {
  if (psk_passphrase_textfield_ &&
      enable_psk_passphrase_ &&
      psk_passphrase_textfield_->visible())
    return GetPassphraseFromField(psk_passphrase_textfield_);
  return std::string();
}

const std::string VPNConfigView::GetUsername() const {
  return GetTextFromField(username_textfield_, true);
}

const std::string VPNConfigView::GetUserPassphrase() const {
  return GetPassphraseFromField(user_passphrase_textfield_);
}

const std::string VPNConfigView::GetGroupName() const {
  return GetTextFromField(group_name_textfield_, false);
}

const std::string VPNConfigView::GetOTP() const {
  return GetTextFromField(otp_textfield_, true);
}

const std::string VPNConfigView::GetServerCACertPEM() const {
  int index = server_ca_cert_combobox_ ?
      server_ca_cert_combobox_->selected_index() : 0;
  if (index == 0) {
    // First item is "Default".
    return std::string();
  } else {
    int cert_index = index - 1;
    return CertLibrary::Get()->GetCertPEMAt(
        CertLibrary::CERT_TYPE_SERVER_CA, cert_index);
  }
}

const std::string VPNConfigView::GetUserCertID() const {
  if (!HaveUserCerts()) {
    return std::string();  // "None installed"
  } else {
    // Certificates are listed in the order they appear in the model.
    int index = user_cert_combobox_ ? user_cert_combobox_->selected_index() : 0;
    return CertLibrary::Get()->GetCertPkcs11IdAt(
        CertLibrary::CERT_TYPE_USER, index);
  }
}

bool VPNConfigView::GetSaveCredentials() const {
  return save_credentials_checkbox_->checked();
}

int VPNConfigView::GetProviderTypeIndex() const {
  if (provider_type_combobox_)
    return provider_type_combobox_->selected_index();
  return provider_type_index_;
}

std::string VPNConfigView::GetProviderTypeString() const {
  int index = GetProviderTypeIndex();
  switch (index) {
    case PROVIDER_TYPE_INDEX_L2TP_IPSEC_PSK:
    case PROVIDER_TYPE_INDEX_L2TP_IPSEC_USER_CERT:
      return shill::kProviderL2tpIpsec;
    case PROVIDER_TYPE_INDEX_OPEN_VPN:
      return shill::kProviderOpenVpn;
  }
  NOTREACHED();
  return std::string();
}

void VPNConfigView::Init() {
  const NetworkState* vpn = NULL;
  if (!service_path_.empty()) {
    vpn = NetworkHandler::Get()->network_state_handler()->
        GetNetworkState(service_path_);
    DCHECK(vpn && vpn->type() == shill::kTypeVPN);
  }
  layout_ = views::GridLayout::CreatePanel(this);
  SetLayoutManager(layout_);

  // Observer any changes to the certificate list.
  CertLibrary::Get()->AddObserver(this);

  views::ColumnSet* column_set = layout_->AddColumnSet(0);
  // Label.
  column_set->AddColumn(views::GridLayout::LEADING, views::GridLayout::FILL, 1,
                        views::GridLayout::USE_PREF, 0, 0);
  column_set->AddPaddingColumn(0, views::kRelatedControlSmallHorizontalSpacing);
  // Textfield, combobox.
  column_set->AddColumn(views::GridLayout::FILL, views::GridLayout::FILL, 1,
                        views::GridLayout::USE_PREF, 0,
                        ChildNetworkConfigView::kInputFieldMinWidth);
  column_set->AddPaddingColumn(0, views::kRelatedControlSmallHorizontalSpacing);
  // Policy indicator.
  column_set->AddColumn(views::GridLayout::CENTER, views::GridLayout::CENTER, 0,
                        views::GridLayout::USE_PREF, 0, 0);

  // Initialize members.
  service_text_modified_ = false;
  title_ = vpn ? IDS_OPTIONS_SETTINGS_JOIN_VPN : IDS_OPTIONS_SETTINGS_ADD_VPN;

  // By default enable all controls.
  enable_psk_passphrase_ = true;
  enable_user_cert_ = true;
  enable_server_ca_cert_ = true;
  enable_otp_ = true;
  enable_group_name_ = true;

  // Server label and input.
  layout_->StartRow(0, 0);
  views::View* server_label =
      new views::Label(l10n_util::GetStringUTF16(
          IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_VPN_SERVER_HOSTNAME));
  layout_->AddView(server_label);
  server_textfield_ = new views::Textfield();
  server_textfield_->set_controller(this);
  layout_->AddView(server_textfield_);
  layout_->AddPaddingRow(0, views::kRelatedControlVerticalSpacing);
  if (!service_path_.empty()) {
    server_label->SetEnabled(false);
    server_textfield_->SetEnabled(false);
  }

  // Service label and name or input.
  layout_->StartRow(0, 0);
  layout_->AddView(new views::Label(l10n_util::GetStringUTF16(
      IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_VPN_SERVICE_NAME)));
  if (service_path_.empty()) {
    service_textfield_ = new views::Textfield();
    service_textfield_->set_controller(this);
    layout_->AddView(service_textfield_);
    service_text_ = NULL;
  } else {
    service_text_ = new views::Label();
    service_text_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
    layout_->AddView(service_text_);
    service_textfield_ = NULL;
  }
  layout_->AddPaddingRow(0, views::kRelatedControlVerticalSpacing);

  // Provider type label and select.
  layout_->StartRow(0, 0);
  layout_->AddView(new views::Label(l10n_util::GetStringUTF16(
      IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_VPN_PROVIDER_TYPE)));
  if (service_path_.empty()) {
    provider_type_combobox_model_.reset(
        new internal::ProviderTypeComboboxModel);
    provider_type_combobox_ = new views::Combobox(
        provider_type_combobox_model_.get());
    provider_type_combobox_->set_listener(this);
    layout_->AddView(provider_type_combobox_);
    provider_type_text_label_ = NULL;
  } else {
    provider_type_text_label_ = new views::Label();
    provider_type_text_label_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
    layout_->AddView(provider_type_text_label_);
    provider_type_combobox_ = NULL;
  }
  layout_->AddPaddingRow(0, views::kRelatedControlVerticalSpacing);

  // PSK passphrase label, input and visible button.
  layout_->StartRow(0, 0);
  psk_passphrase_label_ =  new views::Label(l10n_util::GetStringUTF16(
      IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_VPN_PSK_PASSPHRASE));
  layout_->AddView(psk_passphrase_label_);
  psk_passphrase_textfield_ = new PassphraseTextfield();
  psk_passphrase_textfield_->set_controller(this);
  layout_->AddView(psk_passphrase_textfield_);
  layout_->AddView(
      new ControlledSettingIndicatorView(psk_passphrase_ui_data_));
  layout_->AddPaddingRow(0, views::kRelatedControlVerticalSpacing);

  // Server CA certificate
  if (service_path_.empty()) {
    layout_->StartRow(0, 0);
    server_ca_cert_label_ = new views::Label(l10n_util::GetStringUTF16(
        IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_CERT_SERVER_CA));
    layout_->AddView(server_ca_cert_label_);
    server_ca_cert_combobox_model_.reset(
        new internal::VpnServerCACertComboboxModel());
    server_ca_cert_combobox_ = new views::Combobox(
        server_ca_cert_combobox_model_.get());
    layout_->AddView(server_ca_cert_combobox_);
    layout_->AddView(new ControlledSettingIndicatorView(ca_cert_ui_data_));
    layout_->AddPaddingRow(0, views::kRelatedControlVerticalSpacing);
  } else {
    server_ca_cert_label_ = NULL;
    server_ca_cert_combobox_ = NULL;
  }

  // User certificate label and input.
  layout_->StartRow(0, 0);
  user_cert_label_ = new views::Label(l10n_util::GetStringUTF16(
      IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_VPN_USER_CERT));
  layout_->AddView(user_cert_label_);
  user_cert_combobox_model_.reset(
      new internal::VpnUserCertComboboxModel());
  user_cert_combobox_ = new views::Combobox(user_cert_combobox_model_.get());
  user_cert_combobox_->set_listener(this);
  layout_->AddView(user_cert_combobox_);
  layout_->AddView(new ControlledSettingIndicatorView(user_cert_ui_data_));
  layout_->AddPaddingRow(0, views::kRelatedControlVerticalSpacing);

  // Username label and input.
  layout_->StartRow(0, 0);
  layout_->AddView(new views::Label(l10n_util::GetStringUTF16(
      IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_VPN_USERNAME)));
  username_textfield_ = new views::Textfield();
  username_textfield_->set_controller(this);
  username_textfield_->SetEnabled(username_ui_data_.IsEditable());
  layout_->AddView(username_textfield_);
  layout_->AddView(new ControlledSettingIndicatorView(username_ui_data_));
  layout_->AddPaddingRow(0, views::kRelatedControlVerticalSpacing);

  // User passphrase label, input and visble button.
  layout_->StartRow(0, 0);
  layout_->AddView(new views::Label(l10n_util::GetStringUTF16(
      IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_VPN_USER_PASSPHRASE)));
  user_passphrase_textfield_ = new PassphraseTextfield();
  user_passphrase_textfield_->set_controller(this);
  user_passphrase_textfield_->SetEnabled(user_passphrase_ui_data_.IsEditable());
  layout_->AddView(user_passphrase_textfield_);
  layout_->AddView(
      new ControlledSettingIndicatorView(user_passphrase_ui_data_));
  layout_->AddPaddingRow(0, views::kRelatedControlVerticalSpacing);

  // OTP label and input.
  layout_->StartRow(0, 0);
  otp_label_ = new views::Label(l10n_util::GetStringUTF16(
      IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_VPN_OTP));
  layout_->AddView(otp_label_);
  otp_textfield_ = new views::Textfield();
  otp_textfield_->set_controller(this);
  layout_->AddView(otp_textfield_);
  layout_->AddPaddingRow(0, views::kRelatedControlVerticalSpacing);

  // Group Name label and input.
  layout_->StartRow(0, 0);
  group_name_label_ = new views::Label(l10n_util::GetStringUTF16(
      IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_VPN_GROUP_NAME));
  layout_->AddView(group_name_label_);
  group_name_textfield_ =
      new views::Textfield();
  group_name_textfield_->set_controller(this);
  layout_->AddView(group_name_textfield_);
  layout_->AddView(new ControlledSettingIndicatorView(group_name_ui_data_));
  layout_->AddPaddingRow(0, views::kRelatedControlVerticalSpacing);

  // Save credentials
  layout_->StartRow(0, 0);
  save_credentials_checkbox_ = new views::Checkbox(
      l10n_util::GetStringUTF16(
          IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_SAVE_CREDENTIALS));
  save_credentials_checkbox_->SetEnabled(
      save_credentials_ui_data_.IsEditable());
  layout_->SkipColumns(1);
  layout_->AddView(save_credentials_checkbox_);
  layout_->AddView(
      new ControlledSettingIndicatorView(save_credentials_ui_data_));

  // Error label.
  layout_->StartRow(0, 0);
  layout_->SkipColumns(1);
  error_label_ = new views::Label();
  error_label_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
  error_label_->SetEnabledColor(SK_ColorRED);
  layout_->AddView(error_label_);

  // Set or hide the UI, update comboboxes and error labels.
  Refresh();

  if (vpn) {
    NetworkHandler::Get()->network_configuration_handler()->GetProperties(
        service_path_,
        base::Bind(&VPNConfigView::InitFromProperties,
                   weak_ptr_factory_.GetWeakPtr()),
        base::Bind(&VPNConfigView::GetPropertiesError,
                   weak_ptr_factory_.GetWeakPtr()));
  }
}

void VPNConfigView::InitFromProperties(
    const std::string& service_path,
    const base::DictionaryValue& service_properties) {
  const NetworkState* vpn = NetworkHandler::Get()->network_state_handler()->
      GetNetworkState(service_path);
  if (!vpn) {
    NET_LOG_ERROR("Shill Error getting properties VpnConfigView", service_path);
    return;
  }

  std::string provider_type, server_hostname, username, group_name;
  bool psk_passphrase_required = false;
  const base::DictionaryValue* provider_properties;
  if (service_properties.GetDictionaryWithoutPathExpansion(
          shill::kProviderProperty, &provider_properties)) {
    provider_properties->GetStringWithoutPathExpansion(
        shill::kTypeProperty, &provider_type);
    provider_properties->GetStringWithoutPathExpansion(
        shill::kHostProperty, &server_hostname);
  }
  if (provider_type == shill::kProviderL2tpIpsec) {
    provider_properties->GetStringWithoutPathExpansion(
        shill::kL2tpIpsecClientCertIdProperty, &client_cert_id_);
    ca_cert_pem_ = GetPemFromDictionary(
        provider_properties, shill::kL2tpIpsecCaCertPemProperty);
    provider_properties->GetBooleanWithoutPathExpansion(
        shill::kL2tpIpsecPskRequiredProperty, &psk_passphrase_required);
    provider_properties->GetStringWithoutPathExpansion(
        shill::kL2tpIpsecUserProperty, &username);
    provider_properties->GetStringWithoutPathExpansion(
        shill::kL2tpIpsecTunnelGroupProperty, &group_name);
  } else if (provider_type == shill::kProviderOpenVpn) {
    provider_properties->GetStringWithoutPathExpansion(
        shill::kOpenVPNClientCertIdProperty, &client_cert_id_);
    ca_cert_pem_ = GetPemFromDictionary(
        provider_properties, shill::kOpenVPNCaCertPemProperty);
    provider_properties->GetStringWithoutPathExpansion(
        shill::kOpenVPNUserProperty, &username);
  }
  bool save_credentials = false;
  service_properties.GetBooleanWithoutPathExpansion(
      shill::kSaveCredentialsProperty, &save_credentials);

  provider_type_index_ = ProviderTypeToIndex(provider_type, client_cert_id_);

  if (service_text_)
    service_text_->SetText(base::ASCIIToUTF16(vpn->name()));
  if (provider_type_text_label_)
    provider_type_text_label_->SetText(
        ProviderTypeIndexToString(provider_type_index_));

  if (server_textfield_ && !server_hostname.empty())
    server_textfield_->SetText(base::UTF8ToUTF16(server_hostname));
  if (username_textfield_ && !username.empty())
    username_textfield_->SetText(base::UTF8ToUTF16(username));
  if (group_name_textfield_ && !group_name.empty())
    group_name_textfield_->SetText(base::UTF8ToUTF16(group_name));
  if (psk_passphrase_textfield_)
    psk_passphrase_textfield_->SetShowFake(!psk_passphrase_required);
  if (save_credentials_checkbox_)
    save_credentials_checkbox_->SetChecked(save_credentials);

  Refresh();
}

void VPNConfigView::ParseUIProperties(const NetworkState* vpn) {
  std::string type_dict_name =
      ProviderTypeIndexToONCDictKey(provider_type_index_);
  if (provider_type_index_ == PROVIDER_TYPE_INDEX_L2TP_IPSEC_PSK) {
    ParseVPNUIProperty(vpn, type_dict_name, ::onc::ipsec::kServerCARef,
                       &ca_cert_ui_data_);
    ParseVPNUIProperty(vpn, type_dict_name, ::onc::ipsec::kPSK,
                       &psk_passphrase_ui_data_);
    ParseVPNUIProperty(vpn, type_dict_name, ::onc::ipsec::kGroup,
                       &group_name_ui_data_);
  } else if (provider_type_index_ == PROVIDER_TYPE_INDEX_OPEN_VPN) {
    ParseVPNUIProperty(vpn, type_dict_name, ::onc::openvpn::kServerCARef,
                       &ca_cert_ui_data_);
  }
  ParseVPNUIProperty(vpn, type_dict_name, ::onc::vpn::kClientCertRef,
                     &user_cert_ui_data_);

  const std::string credentials_dict_name(
      provider_type_index_ == PROVIDER_TYPE_INDEX_L2TP_IPSEC_PSK ?
      ::onc::vpn::kL2TP : type_dict_name);
  ParseVPNUIProperty(vpn, credentials_dict_name, ::onc::vpn::kUsername,
                     &username_ui_data_);
  ParseVPNUIProperty(vpn, credentials_dict_name, ::onc::vpn::kPassword,
                     &user_passphrase_ui_data_);
  ParseVPNUIProperty(vpn, credentials_dict_name, ::onc::vpn::kSaveCredentials,
                     &save_credentials_ui_data_);
}

void VPNConfigView::GetPropertiesError(
    const std::string& error_name,
    scoped_ptr<base::DictionaryValue> error_data) {
  NET_LOG_ERROR("Shill Error from VpnConfigView: " + error_name, "");
}

void VPNConfigView::SetConfigProperties(
    base::DictionaryValue* properties) {
  int provider_type_index = GetProviderTypeIndex();
  std::string user_passphrase = GetUserPassphrase();
  std::string user_name = GetUsername();
  std::string group_name = GetGroupName();
  switch (provider_type_index) {
    case PROVIDER_TYPE_INDEX_L2TP_IPSEC_PSK: {
      std::string psk_passphrase = GetPSKPassphrase();
      if (!psk_passphrase.empty()) {
        properties->SetStringWithoutPathExpansion(
            shill::kL2tpIpsecPskProperty, GetPSKPassphrase());
      }
      if (!group_name.empty()) {
        properties->SetStringWithoutPathExpansion(
            shill::kL2tpIpsecTunnelGroupProperty, group_name);
      }
      if (!user_name.empty()) {
        properties->SetStringWithoutPathExpansion(
            shill::kL2tpIpsecUserProperty, user_name);
      }
      if (!user_passphrase.empty()) {
        properties->SetStringWithoutPathExpansion(
            shill::kL2tpIpsecPasswordProperty, user_passphrase);
      }
      break;
    }
    case PROVIDER_TYPE_INDEX_L2TP_IPSEC_USER_CERT: {
      std::string ca_cert_pem = GetServerCACertPEM();
      if (!ca_cert_pem.empty()) {
        base::ListValue* pem_list = new base::ListValue;
        pem_list->AppendString(ca_cert_pem);
        properties->SetWithoutPathExpansion(
            shill::kL2tpIpsecCaCertPemProperty, pem_list);
      }
      properties->SetStringWithoutPathExpansion(
          shill::kL2tpIpsecClientCertIdProperty, GetUserCertID());
      if (!group_name.empty()) {
        properties->SetStringWithoutPathExpansion(
            shill::kL2tpIpsecTunnelGroupProperty, GetGroupName());
      }
      if (!user_name.empty()) {
        properties->SetStringWithoutPathExpansion(
            shill::kL2tpIpsecUserProperty, user_name);
      }
      if (!user_passphrase.empty()) {
        properties->SetStringWithoutPathExpansion(
            shill::kL2tpIpsecPasswordProperty, user_passphrase);
      }
      break;
    }
    case PROVIDER_TYPE_INDEX_OPEN_VPN: {
      std::string ca_cert_pem = GetServerCACertPEM();
      if (!ca_cert_pem.empty()) {
        base::ListValue* pem_list = new base::ListValue;
        pem_list->AppendString(ca_cert_pem);
        properties->SetWithoutPathExpansion(
            shill::kOpenVPNCaCertPemProperty, pem_list);
      }
      properties->SetStringWithoutPathExpansion(
          shill::kOpenVPNClientCertIdProperty, GetUserCertID());
      properties->SetStringWithoutPathExpansion(
          shill::kOpenVPNUserProperty, GetUsername());
      if (!user_passphrase.empty()) {
        properties->SetStringWithoutPathExpansion(
            shill::kOpenVPNPasswordProperty, user_passphrase);
      }
      std::string otp = GetOTP();
      if (!otp.empty()) {
        properties->SetStringWithoutPathExpansion(
            shill::kOpenVPNOTPProperty, otp);
      }
      break;
    }
    case PROVIDER_TYPE_INDEX_MAX:
      NOTREACHED();
      break;
  }
  properties->SetBooleanWithoutPathExpansion(
      shill::kSaveCredentialsProperty, GetSaveCredentials());
}

void VPNConfigView::Refresh() {
  UpdateControls();

  // Set certificate combo boxes.
  if (server_ca_cert_combobox_) {
    server_ca_cert_combobox_->ModelChanged();
    if (enable_server_ca_cert_ && !ca_cert_pem_.empty()) {
      // Select the current server CA certificate in the combobox.
      int cert_index = CertLibrary::Get()->GetCertIndexByPEM(
          CertLibrary::CERT_TYPE_SERVER_CA, ca_cert_pem_);
      if (cert_index >= 0) {
        // Skip item for "Default"
        server_ca_cert_combobox_->SetSelectedIndex(1 + cert_index);
      } else {
        server_ca_cert_combobox_->SetSelectedIndex(0);
      }
    } else {
      server_ca_cert_combobox_->SetSelectedIndex(0);
    }
  }

  if (user_cert_combobox_) {
    user_cert_combobox_->ModelChanged();
    if (enable_user_cert_ && !client_cert_id_.empty()) {
      int cert_index = CertLibrary::Get()->GetCertIndexByPkcs11Id(
          CertLibrary::CERT_TYPE_USER, client_cert_id_);
      if (cert_index >= 0)
        user_cert_combobox_->SetSelectedIndex(cert_index);
      else
        user_cert_combobox_->SetSelectedIndex(0);
    } else {
      user_cert_combobox_->SetSelectedIndex(0);
    }
  }

  UpdateErrorLabel();
}

void VPNConfigView::UpdateControlsToEnable() {
  enable_psk_passphrase_ = false;
  enable_user_cert_ = false;
  enable_server_ca_cert_ = false;
  enable_otp_ = false;
  enable_group_name_ = false;
  int provider_type_index = GetProviderTypeIndex();
  if (provider_type_index == PROVIDER_TYPE_INDEX_L2TP_IPSEC_PSK) {
    enable_psk_passphrase_ = true;
    enable_group_name_ = true;
  } else if (provider_type_index == PROVIDER_TYPE_INDEX_L2TP_IPSEC_USER_CERT) {
    enable_server_ca_cert_ = true;
    enable_user_cert_ = HaveUserCerts();
    enable_group_name_ = true;
  } else {  // PROVIDER_TYPE_INDEX_OPEN_VPN (default)
    enable_server_ca_cert_ = true;
    enable_user_cert_ = HaveUserCerts();
    enable_otp_ = true;
  }
}

void VPNConfigView::UpdateControls() {
  UpdateControlsToEnable();

  if (psk_passphrase_label_)
    psk_passphrase_label_->SetEnabled(enable_psk_passphrase_);
  if (psk_passphrase_textfield_)
    psk_passphrase_textfield_->SetEnabled(enable_psk_passphrase_ &&
                                          psk_passphrase_ui_data_.IsEditable());

  if (user_cert_label_)
    user_cert_label_->SetEnabled(enable_user_cert_);
  if (user_cert_combobox_)
    user_cert_combobox_->SetEnabled(enable_user_cert_ &&
                                    user_cert_ui_data_.IsEditable());

  if (server_ca_cert_label_)
    server_ca_cert_label_->SetEnabled(enable_server_ca_cert_);
  if (server_ca_cert_combobox_)
    server_ca_cert_combobox_->SetEnabled(enable_server_ca_cert_ &&
                                         ca_cert_ui_data_.IsEditable());

  if (otp_label_)
    otp_label_->SetEnabled(enable_otp_);
  if (otp_textfield_)
    otp_textfield_->SetEnabled(enable_otp_);

  if (group_name_label_)
    group_name_label_->SetEnabled(enable_group_name_);
  if (group_name_textfield_)
    group_name_textfield_->SetEnabled(enable_group_name_ &&
                                      group_name_ui_data_.IsEditable());
}

void VPNConfigView::UpdateErrorLabel() {
  // Error message.
  base::string16 error_msg;
  bool cert_required =
      GetProviderTypeIndex() == PROVIDER_TYPE_INDEX_L2TP_IPSEC_USER_CERT;
  if (cert_required && CertLibrary::Get()->CertificatesLoaded()) {
    if (!HaveUserCerts()) {
      error_msg = l10n_util::GetStringUTF16(
          IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_PLEASE_INSTALL_USER_CERT);
    } else if (!IsUserCertValid()) {
      error_msg = l10n_util::GetStringUTF16(
          IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_REQUIRE_HARDWARE_BACKED);
    }
  }
  if (error_msg.empty() && !service_path_.empty()) {
    // TODO(kuan): differentiate between bad psk and user passphrases.
    const NetworkState* vpn = NetworkHandler::Get()->network_state_handler()->
        GetNetworkState(service_path_);
    if (vpn && vpn->connection_state() == shill::kStateFailure)
      error_msg = ash::network_connect::ErrorString(
          vpn->last_error(), vpn->path());
  }
  if (!error_msg.empty()) {
    error_label_->SetText(error_msg);
    error_label_->SetVisible(true);
  } else {
    error_label_->SetVisible(false);
  }
}

void VPNConfigView::UpdateCanLogin() {
  parent_->GetDialogClientView()->UpdateDialogButtons();
}

bool VPNConfigView::HaveUserCerts() const {
  return CertLibrary::Get()->NumCertificates(CertLibrary::CERT_TYPE_USER) > 0;
}

bool VPNConfigView::IsUserCertValid() const {
  if (!user_cert_combobox_ || !enable_user_cert_)
    return false;
  int index = user_cert_combobox_->selected_index();
  if (index < 0)
    return false;
  // Currently only hardware-backed user certificates are valid.
  if (CertLibrary::Get()->IsHardwareBacked() &&
      !CertLibrary::Get()->IsCertHardwareBackedAt(
          CertLibrary::CERT_TYPE_USER, index))
    return false;
  return true;
}

const std::string VPNConfigView::GetTextFromField(views::Textfield* textfield,
                                                  bool trim_whitespace) const {
  if (!textfield)
    return std::string();
  std::string untrimmed = base::UTF16ToUTF8(textfield->text());
  if (!trim_whitespace)
    return untrimmed;
  std::string result;
  base::TrimWhitespaceASCII(untrimmed, base::TRIM_ALL, &result);
  return result;
}

const std::string VPNConfigView::GetPassphraseFromField(
    PassphraseTextfield* textfield) const {
  if (!textfield)
    return std::string();
  return textfield->GetPassphrase();
}

void VPNConfigView::ParseVPNUIProperty(
    const NetworkState* network,
    const std::string& dict_key,
    const std::string& key,
    NetworkPropertyUIData* property_ui_data) {
  ::onc::ONCSource onc_source = ::onc::ONC_SOURCE_NONE;
  const base::DictionaryValue* onc =
      onc::FindPolicyForActiveUser(network->guid(), &onc_source);

  VLOG_IF(1, !onc) << "No ONC found for VPN network " << network->guid();
  property_ui_data->ParseOncProperty(
      onc_source,
      onc,
      base::StringPrintf("%s.%s.%s",
                         ::onc::network_config::kVPN,
                         dict_key.c_str(),
                         key.c_str()));
}

}  // namespace chromeos

/* [<][>][^][v][top][bottom][index][help] */