vpn_config_view.cc revision ca12bfac764ba476d6cd062bf1dde12cc64c3f40
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/options/vpn_config_view.h"
6
7#include "base/strings/string_util.h"
8#include "base/strings/stringprintf.h"
9#include "base/strings/utf_string_conversions.h"
10#include "chrome/browser/chromeos/cros/network_library.h"
11#include "chrome/browser/chromeos/enrollment_dialog_view.h"
12#include "chrome/browser/profiles/profile_manager.h"
13#include "chrome/common/net/x509_certificate_model.h"
14#include "chromeos/network/network_ui_data.h"
15#include "chromeos/network/onc/onc_constants.h"
16#include "grit/chromium_strings.h"
17#include "grit/generated_resources.h"
18#include "grit/locale_settings.h"
19#include "grit/theme_resources.h"
20#include "ui/base/events/event.h"
21#include "ui/base/l10n/l10n_util.h"
22#include "ui/base/models/combobox_model.h"
23#include "ui/base/resource/resource_bundle.h"
24#include "ui/views/controls/button/checkbox.h"
25#include "ui/views/controls/combobox/combobox.h"
26#include "ui/views/controls/label.h"
27#include "ui/views/controls/textfield/textfield.h"
28#include "ui/views/layout/grid_layout.h"
29#include "ui/views/layout/layout_constants.h"
30#include "ui/views/widget/widget.h"
31#include "ui/views/window/dialog_client_view.h"
32
33namespace {
34
35// Root CA certificates that are built into Chrome use this token name.
36const char* const kRootCertificateTokenName = "Builtin Object Token";
37
38string16 ProviderTypeToString(chromeos::ProviderType type) {
39  switch (type) {
40    case chromeos::PROVIDER_TYPE_L2TP_IPSEC_PSK:
41      return l10n_util::GetStringUTF16(
42          IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_L2TP_IPSEC_PSK);
43    case chromeos::PROVIDER_TYPE_L2TP_IPSEC_USER_CERT:
44      return l10n_util::GetStringUTF16(
45          IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_L2TP_IPSEC_USER_CERT);
46    case chromeos::PROVIDER_TYPE_OPEN_VPN:
47      return l10n_util::GetStringUTF16(
48          IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_OPEN_VPN);
49    case chromeos::PROVIDER_TYPE_MAX:
50      break;
51  }
52  NOTREACHED();
53  return string16();
54}
55
56// Translates the provider type to the name of the respective ONC dictionary
57// containing configuration data for the type.
58std::string ProviderTypeToONCDictKey(chromeos::ProviderType type) {
59  switch (type) {
60    case chromeos::PROVIDER_TYPE_L2TP_IPSEC_PSK:
61    case chromeos::PROVIDER_TYPE_L2TP_IPSEC_USER_CERT:
62      return chromeos::onc::vpn::kIPsec;
63    case chromeos::PROVIDER_TYPE_OPEN_VPN:
64      return chromeos::onc::vpn::kOpenVPN;
65    case chromeos::PROVIDER_TYPE_MAX:
66      break;
67  }
68
69  NOTREACHED() << "Unhandled provider type " << type;
70  return std::string();
71}
72
73}  // namespace
74
75namespace chromeos {
76
77namespace internal {
78
79class ProviderTypeComboboxModel : public ui::ComboboxModel {
80 public:
81  ProviderTypeComboboxModel();
82  virtual ~ProviderTypeComboboxModel();
83
84  // Overridden from ui::ComboboxModel:
85  virtual int GetItemCount() const OVERRIDE;
86  virtual string16 GetItemAt(int index) OVERRIDE;
87
88 private:
89  DISALLOW_COPY_AND_ASSIGN(ProviderTypeComboboxModel);
90};
91
92class VpnServerCACertComboboxModel : public ui::ComboboxModel {
93 public:
94  VpnServerCACertComboboxModel();
95  virtual ~VpnServerCACertComboboxModel();
96
97  // Overridden from ui::ComboboxModel:
98  virtual int GetItemCount() const OVERRIDE;
99  virtual string16 GetItemAt(int index) OVERRIDE;
100
101 private:
102  DISALLOW_COPY_AND_ASSIGN(VpnServerCACertComboboxModel);
103};
104
105class VpnUserCertComboboxModel : public ui::ComboboxModel {
106 public:
107  VpnUserCertComboboxModel();
108  virtual ~VpnUserCertComboboxModel();
109
110  // Overridden from ui::ComboboxModel:
111  virtual int GetItemCount() const OVERRIDE;
112  virtual string16 GetItemAt(int index) OVERRIDE;
113
114 private:
115  DISALLOW_COPY_AND_ASSIGN(VpnUserCertComboboxModel);
116};
117
118// ProviderTypeComboboxModel ---------------------------------------------------
119
120ProviderTypeComboboxModel::ProviderTypeComboboxModel() {
121}
122
123ProviderTypeComboboxModel::~ProviderTypeComboboxModel() {
124}
125
126int ProviderTypeComboboxModel::GetItemCount() const {
127  return PROVIDER_TYPE_MAX;
128}
129
130string16 ProviderTypeComboboxModel::GetItemAt(int index) {
131  ProviderType type = static_cast<ProviderType>(index);
132  return ProviderTypeToString(type);
133}
134
135// VpnServerCACertComboboxModel ------------------------------------------------
136
137VpnServerCACertComboboxModel::VpnServerCACertComboboxModel() {
138}
139
140VpnServerCACertComboboxModel::~VpnServerCACertComboboxModel() {
141}
142
143int VpnServerCACertComboboxModel::GetItemCount() const {
144  if (CertLibrary::Get()->CertificatesLoading())
145    return 1;  // "Loading"
146  // "Default" + certs.
147  return CertLibrary::Get()->NumCertificates(
148      CertLibrary::CERT_TYPE_SERVER_CA) + 1;
149}
150
151string16 VpnServerCACertComboboxModel::GetItemAt(int index) {
152  if (CertLibrary::Get()->CertificatesLoading())
153    return l10n_util::GetStringUTF16(
154        IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_CERT_LOADING);
155  if (index == 0)
156    return l10n_util::GetStringUTF16(
157        IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_CERT_SERVER_CA_DEFAULT);
158  int cert_index = index - 1;
159  return CertLibrary::Get()->GetCertDisplayStringAt(
160      CertLibrary::CERT_TYPE_SERVER_CA, cert_index);
161}
162
163// VpnUserCertComboboxModel ----------------------------------------------------
164
165VpnUserCertComboboxModel::VpnUserCertComboboxModel() {
166}
167
168VpnUserCertComboboxModel::~VpnUserCertComboboxModel() {
169}
170
171int VpnUserCertComboboxModel::GetItemCount() const {
172  if (CertLibrary::Get()->CertificatesLoading())
173    return 1;  // "Loading"
174  int num_certs =
175      CertLibrary::Get()->NumCertificates(CertLibrary::CERT_TYPE_USER);
176  if (num_certs == 0)
177    return 1;  // "None installed"
178  return num_certs;
179}
180
181string16 VpnUserCertComboboxModel::GetItemAt(int index) {
182  if (CertLibrary::Get()->CertificatesLoading()) {
183    return l10n_util::GetStringUTF16(
184        IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_CERT_LOADING);
185  }
186  if (CertLibrary::Get()->NumCertificates(CertLibrary::CERT_TYPE_USER) == 0) {
187    return l10n_util::GetStringUTF16(
188        IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_USER_CERT_NONE_INSTALLED);
189  }
190  return CertLibrary::Get()->GetCertDisplayStringAt(
191      CertLibrary::CERT_TYPE_USER, index);
192}
193
194}  // namespace internal
195
196VPNConfigView::VPNConfigView(NetworkConfigView* parent, VirtualNetwork* vpn)
197    : ChildNetworkConfigView(parent, vpn),
198      title_(0) {
199  Init(vpn);
200}
201
202VPNConfigView::VPNConfigView(NetworkConfigView* parent)
203    : ChildNetworkConfigView(parent),
204      title_(0) {
205  Init(NULL);
206}
207
208VPNConfigView::~VPNConfigView() {
209  CertLibrary::Get()->RemoveObserver(this);
210}
211
212string16 VPNConfigView::GetTitle() const {
213  DCHECK_NE(title_, 0);
214  return l10n_util::GetStringUTF16(title_);
215}
216
217views::View* VPNConfigView::GetInitiallyFocusedView() {
218  // Put focus in the first editable field.
219  if (server_textfield_)
220    return server_textfield_;
221  else if (service_textfield_)
222    return service_textfield_;
223  else if (provider_type_combobox_)
224    return provider_type_combobox_;
225  else if (psk_passphrase_textfield_ && psk_passphrase_textfield_->enabled())
226    return psk_passphrase_textfield_;
227  else if (user_cert_combobox_ && user_cert_combobox_->enabled())
228    return user_cert_combobox_;
229  else if (server_ca_cert_combobox_ && server_ca_cert_combobox_->enabled())
230    return server_ca_cert_combobox_;
231  else
232    return NULL;
233}
234
235bool VPNConfigView::CanLogin() {
236  // Username is always required.
237  if (GetUsername().empty())
238    return false;
239
240  // TODO(stevenjb): min kMinPassphraseLen length?
241  if (service_path_.empty() &&
242      (GetService().empty() || GetServer().empty()))
243    return false;
244
245  // Block login if certs are required but user has none.
246  if (UserCertRequired() && (!HaveUserCerts() || !IsUserCertValid()))
247    return false;
248
249  return true;
250}
251
252void VPNConfigView::ContentsChanged(views::Textfield* sender,
253                                    const string16& new_contents) {
254  if (sender == server_textfield_ && !service_text_modified_) {
255    // Set the service name to the server name up to '.', unless it has
256    // been explicitly set by the user.
257    string16 server = server_textfield_->text();
258    string16::size_type n = server.find_first_of(L'.');
259    service_name_from_server_ = server.substr(0, n);
260    service_textfield_->SetText(service_name_from_server_);
261  }
262  if (sender == service_textfield_) {
263    if (new_contents.empty())
264      service_text_modified_ = false;
265    else if (new_contents != service_name_from_server_)
266      service_text_modified_ = true;
267  }
268  UpdateCanLogin();
269}
270
271bool VPNConfigView::HandleKeyEvent(views::Textfield* sender,
272                                   const ui::KeyEvent& key_event) {
273  if ((sender == psk_passphrase_textfield_ ||
274       sender == user_passphrase_textfield_) &&
275      key_event.key_code() == ui::VKEY_RETURN) {
276    parent_->GetDialogClientView()->AcceptWindow();
277  }
278  return false;
279}
280
281void VPNConfigView::ButtonPressed(views::Button* sender,
282                                  const ui::Event& event) {
283}
284
285void VPNConfigView::OnSelectedIndexChanged(views::Combobox* combobox) {
286  if (combobox == provider_type_combobox_) {
287    provider_type_ = static_cast<ProviderType>(combobox->selected_index());
288    UpdateControls();
289  } else if (combobox == user_cert_combobox_ ||
290             combobox == server_ca_cert_combobox_) {
291    // Do nothing.
292  } else {
293    NOTREACHED();
294  }
295  UpdateErrorLabel();
296  UpdateCanLogin();
297}
298
299void VPNConfigView::OnCertificatesLoaded(bool initial_load) {
300  Refresh();
301}
302
303bool VPNConfigView::Login() {
304  NetworkLibrary* cros = NetworkLibrary::Get();
305  if (service_path_.empty()) {
306    NetworkLibrary::VPNConfigData config_data;
307    switch (provider_type_) {
308      case PROVIDER_TYPE_L2TP_IPSEC_PSK:
309        config_data.psk = GetPSKPassphrase();
310        config_data.username = GetUsername();
311        config_data.user_passphrase = GetUserPassphrase();
312        config_data.group_name = GetGroupName();
313        break;
314      case PROVIDER_TYPE_L2TP_IPSEC_USER_CERT: {
315        config_data.server_ca_cert_pem = GetServerCACertPEM();
316        config_data.client_cert_pkcs11_id = GetUserCertID();
317        config_data.username = GetUsername();
318        config_data.user_passphrase = GetUserPassphrase();
319        config_data.group_name = GetGroupName();
320        break;
321      }
322      case PROVIDER_TYPE_OPEN_VPN:
323        config_data.server_ca_cert_pem = GetServerCACertPEM();
324        config_data.client_cert_pkcs11_id = GetUserCertID();
325        config_data.username = GetUsername();
326        config_data.user_passphrase = GetUserPassphrase();
327        config_data.otp = GetOTP();
328        break;
329      case PROVIDER_TYPE_MAX:
330        break;
331    }
332    config_data.save_credentials = GetSaveCredentials();
333    cros->ConnectToUnconfiguredVirtualNetwork(
334        GetService(), GetServer(), provider_type_, config_data);
335  } else {
336    VirtualNetwork* vpn = cros->FindVirtualNetworkByPath(service_path_);
337    if (!vpn) {
338      // TODO(stevenjb): Add notification for this.
339      LOG(WARNING) << "VPN no longer exists: " << service_path_;
340      return true;  // Close dialog.
341    }
342    switch (provider_type_) {
343      case PROVIDER_TYPE_L2TP_IPSEC_PSK:
344        vpn->SetL2TPIPsecPSKCredentials(GetPSKPassphrase(),
345                                        GetUsername(),
346                                        GetUserPassphrase(),
347                                        GetGroupName());
348        break;
349      case PROVIDER_TYPE_L2TP_IPSEC_USER_CERT: {
350        vpn->SetL2TPIPsecCertCredentials(GetUserCertID(),
351                                         GetUsername(),
352                                         GetUserPassphrase(),
353                                         GetGroupName());
354        break;
355      }
356      case PROVIDER_TYPE_OPEN_VPN: {
357        vpn->SetOpenVPNCredentials(GetUserCertID(),
358                                   GetUsername(),
359                                   GetUserPassphrase(),
360                                   GetOTP());
361        break;
362      }
363      case PROVIDER_TYPE_MAX:
364        break;
365    }
366    vpn->SetEnrollmentDelegate(
367        CreateEnrollmentDelegate(GetWidget()->GetNativeWindow(),
368                                 vpn->name(),
369                                 ProfileManager::GetLastUsedProfile()));
370    vpn->SetSaveCredentials(GetSaveCredentials());
371    cros->ConnectToVirtualNetwork(vpn);
372  }
373  // Connection failures are responsible for updating the UI, including
374  // reopening dialogs.
375  return true;  // Close dialog.
376}
377
378void VPNConfigView::Cancel() {
379}
380
381void VPNConfigView::InitFocus() {
382  views::View* view_to_focus = GetInitiallyFocusedView();
383  if (view_to_focus)
384    view_to_focus->RequestFocus();
385}
386
387const std::string VPNConfigView::GetService() const {
388  if (service_textfield_ != NULL)
389    return GetTextFromField(service_textfield_, true);
390  return service_path_;
391}
392
393const std::string VPNConfigView::GetServer() const {
394  if (server_textfield_ != NULL)
395    return GetTextFromField(server_textfield_, true);
396  return server_hostname_;
397}
398
399const std::string VPNConfigView::GetPSKPassphrase() const {
400  if (psk_passphrase_textfield_ &&
401      enable_psk_passphrase_ &&
402      psk_passphrase_textfield_->visible())
403    return GetPassphraseFromField(psk_passphrase_textfield_);
404  return std::string();
405}
406
407const std::string VPNConfigView::GetUsername() const {
408  return GetTextFromField(username_textfield_, true);
409}
410
411const std::string VPNConfigView::GetUserPassphrase() const {
412  return GetPassphraseFromField(user_passphrase_textfield_);
413}
414
415const std::string VPNConfigView::GetGroupName() const {
416  return GetTextFromField(group_name_textfield_, false);
417}
418
419const std::string VPNConfigView::GetOTP() const {
420  return GetTextFromField(otp_textfield_, true);
421}
422
423const std::string VPNConfigView::GetServerCACertPEM() const {
424  int index = server_ca_cert_combobox_ ?
425      server_ca_cert_combobox_->selected_index() : 0;
426  if (index == 0) {
427    // First item is "Default".
428    return std::string();
429  } else {
430    int cert_index = index - 1;
431    return CertLibrary::Get()->GetCertPEMAt(
432        CertLibrary::CERT_TYPE_SERVER_CA, cert_index);
433  }
434}
435
436const std::string VPNConfigView::GetUserCertID() const {
437  if (!HaveUserCerts()) {
438    return std::string();  // "None installed"
439  } else {
440    // Certificates are listed in the order they appear in the model.
441    int index = user_cert_combobox_ ? user_cert_combobox_->selected_index() : 0;
442    return CertLibrary::Get()->GetCertPkcs11IdAt(
443        CertLibrary::CERT_TYPE_USER, index);
444  }
445}
446
447bool VPNConfigView::GetSaveCredentials() const {
448  return save_credentials_checkbox_->checked();
449}
450
451void VPNConfigView::Init(VirtualNetwork* vpn) {
452  if (vpn) {
453    ProviderType type = vpn->provider_type();
454    std::string type_dict_name = ProviderTypeToONCDictKey(type);
455
456    if (type == PROVIDER_TYPE_L2TP_IPSEC_PSK) {
457      ParseVPNUIProperty(&ca_cert_ui_data_, vpn, type_dict_name,
458                         onc::ipsec::kServerCARef);
459      ParseVPNUIProperty(&psk_passphrase_ui_data_, vpn, type_dict_name,
460                         onc::ipsec::kPSK);
461      ParseVPNUIProperty(&group_name_ui_data_, vpn, type_dict_name,
462                         onc::ipsec::kGroup);
463    } else { // OpenVPN
464      ParseVPNUIProperty(&ca_cert_ui_data_, vpn, type_dict_name,
465                         onc::openvpn::kServerCARef);
466    }
467    ParseVPNUIProperty(&user_cert_ui_data_, vpn, type_dict_name,
468                       onc::vpn::kClientCertRef);
469
470    const std::string credentials_dict_name(
471        type == PROVIDER_TYPE_L2TP_IPSEC_PSK ?
472            onc::vpn::kL2TP : type_dict_name);
473    ParseVPNUIProperty(&username_ui_data_, vpn, credentials_dict_name,
474                       onc::vpn::kUsername);
475    ParseVPNUIProperty(&user_passphrase_ui_data_, vpn, credentials_dict_name,
476                       onc::vpn::kPassword);
477    ParseVPNUIProperty(&save_credentials_ui_data_, vpn, credentials_dict_name,
478                       onc::vpn::kSaveCredentials);
479  }
480
481  views::GridLayout* layout = views::GridLayout::CreatePanel(this);
482  SetLayoutManager(layout);
483
484  // Observer any changes to the certificate list.
485  CertLibrary::Get()->AddObserver(this);
486
487  const int column_view_set_id = 0;
488  views::ColumnSet* column_set = layout->AddColumnSet(column_view_set_id);
489  // Label.
490  column_set->AddColumn(views::GridLayout::LEADING, views::GridLayout::FILL, 1,
491                        views::GridLayout::USE_PREF, 0, 0);
492  column_set->AddPaddingColumn(0, views::kRelatedControlSmallHorizontalSpacing);
493  // Textfield, combobox.
494  column_set->AddColumn(views::GridLayout::FILL, views::GridLayout::FILL, 1,
495                        views::GridLayout::USE_PREF, 0,
496                        ChildNetworkConfigView::kInputFieldMinWidth);
497  column_set->AddPaddingColumn(0, views::kRelatedControlSmallHorizontalSpacing);
498  // Policy indicator.
499  column_set->AddColumn(views::GridLayout::CENTER, views::GridLayout::CENTER, 0,
500                        views::GridLayout::USE_PREF, 0, 0);
501
502  // Initialize members.
503  service_text_modified_ = false;
504  if (vpn) {
505    provider_type_ = vpn->provider_type();
506    // Sets enable_* based on the provider type which we use to control
507    // which controls to make visible.
508    UpdateControlsToEnable();
509  } else {
510    // Set the default provider type.
511    provider_type_ = PROVIDER_TYPE_L2TP_IPSEC_PSK;
512    // Provider Type is user selectable, so enable all controls during init.
513    enable_psk_passphrase_ = true;
514    enable_user_cert_ = true;
515    enable_server_ca_cert_ = true;
516    enable_otp_ = true;
517    enable_group_name_ = true;
518  }
519
520  // Initialize the title string ID used for the dialog.
521  title_ = vpn ? IDS_OPTIONS_SETTINGS_JOIN_VPN : IDS_OPTIONS_SETTINGS_ADD_VPN;
522
523  // Server label and input.
524  // Only provide Server name when configuring a new VPN.
525  if (!vpn) {
526    layout->StartRow(0, column_view_set_id);
527    layout->AddView(new views::Label(l10n_util::GetStringUTF16(
528      IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_VPN_SERVER_HOSTNAME)));
529    server_textfield_ = new views::Textfield(views::Textfield::STYLE_DEFAULT);
530    server_textfield_->SetController(this);
531    layout->AddView(server_textfield_);
532    layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing);
533  } else {
534    server_textfield_ = NULL;
535  }
536
537  // Service label and name or input.
538  layout->StartRow(0, column_view_set_id);
539  layout->AddView(new views::Label(l10n_util::GetStringUTF16(
540      IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_VPN_SERVICE_NAME)));
541  if (!vpn) {
542    service_textfield_ = new views::Textfield(views::Textfield::STYLE_DEFAULT);
543    service_textfield_->SetController(this);
544    layout->AddView(service_textfield_);
545    service_text_ = NULL;
546  } else {
547    service_text_ = new views::Label(ASCIIToUTF16(vpn->name()));
548    service_text_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
549    layout->AddView(service_text_);
550    service_textfield_ = NULL;
551  }
552  layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing);
553
554  // Provider type label and select.
555  layout->StartRow(0, column_view_set_id);
556  layout->AddView(new views::Label(l10n_util::GetStringUTF16(
557      IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_VPN_PROVIDER_TYPE)));
558  if (!vpn) {
559    provider_type_combobox_model_.reset(
560        new internal::ProviderTypeComboboxModel);
561    provider_type_combobox_ = new views::Combobox(
562        provider_type_combobox_model_.get());
563    provider_type_combobox_->set_listener(this);
564    layout->AddView(provider_type_combobox_);
565    provider_type_text_label_ = NULL;
566  } else {
567    provider_type_text_label_ =
568        new views::Label(ProviderTypeToString(provider_type_));
569    provider_type_text_label_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
570    layout->AddView(provider_type_text_label_);
571    provider_type_combobox_ = NULL;
572  }
573  layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing);
574
575  // PSK passphrase label, input and visible button.
576  if (enable_psk_passphrase_) {
577    layout->StartRow(0, column_view_set_id);
578    psk_passphrase_label_ =  new views::Label(l10n_util::GetStringUTF16(
579        IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_VPN_PSK_PASSPHRASE));
580    layout->AddView(psk_passphrase_label_);
581    bool has_psk_passphrase = vpn && !vpn->IsPSKPassphraseRequired();
582    psk_passphrase_textfield_ = new PassphraseTextfield(has_psk_passphrase);
583    psk_passphrase_textfield_->SetController(this);
584    layout->AddView(psk_passphrase_textfield_);
585    layout->AddView(
586        new ControlledSettingIndicatorView(psk_passphrase_ui_data_));
587    layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing);
588  } else {
589    psk_passphrase_label_ = NULL;
590    psk_passphrase_textfield_ = NULL;
591  }
592
593  // Server CA certificate
594  // Only provide Server CA when configuring a new VPN.
595  if (!vpn) {
596    layout->StartRow(0, column_view_set_id);
597    server_ca_cert_label_ =
598        new views::Label(l10n_util::GetStringUTF16(
599            IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_CERT_SERVER_CA));
600    layout->AddView(server_ca_cert_label_);
601    server_ca_cert_combobox_model_.reset(
602        new internal::VpnServerCACertComboboxModel());
603    server_ca_cert_combobox_ = new views::Combobox(
604        server_ca_cert_combobox_model_.get());
605    layout->AddView(server_ca_cert_combobox_);
606    layout->AddView(new ControlledSettingIndicatorView(ca_cert_ui_data_));
607    layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing);
608  } else {
609    server_ca_cert_label_ = NULL;
610    server_ca_cert_combobox_ = NULL;
611  }
612
613  // User certificate label and input.
614  if (enable_user_cert_) {
615    layout->StartRow(0, column_view_set_id);
616    user_cert_label_ = new views::Label(l10n_util::GetStringUTF16(
617        IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_VPN_USER_CERT));
618    layout->AddView(user_cert_label_);
619    user_cert_combobox_model_.reset(
620        new internal::VpnUserCertComboboxModel());
621    user_cert_combobox_ = new views::Combobox(user_cert_combobox_model_.get());
622    user_cert_combobox_->set_listener(this);
623    layout->AddView(user_cert_combobox_);
624    layout->AddView(new ControlledSettingIndicatorView(user_cert_ui_data_));
625    layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing);
626  } else {
627    user_cert_label_ = NULL;
628    user_cert_combobox_ = NULL;
629  }
630
631  // Username label and input.
632  layout->StartRow(0, column_view_set_id);
633  layout->AddView(new views::Label(l10n_util::GetStringUTF16(
634      IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_VPN_USERNAME)));
635  username_textfield_ = new views::Textfield(views::Textfield::STYLE_DEFAULT);
636  username_textfield_->SetController(this);
637  username_textfield_->SetEnabled(username_ui_data_.IsEditable());
638  if (vpn && !vpn->username().empty())
639    username_textfield_->SetText(UTF8ToUTF16(vpn->username()));
640  layout->AddView(username_textfield_);
641  layout->AddView(new ControlledSettingIndicatorView(username_ui_data_));
642  layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing);
643
644  // User passphrase label, input and visble button.
645  layout->StartRow(0, column_view_set_id);
646  layout->AddView(new views::Label(l10n_util::GetStringUTF16(
647      IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_VPN_USER_PASSPHRASE)));
648  bool has_user_passphrase = vpn && !vpn->IsUserPassphraseRequired();
649  user_passphrase_textfield_ = new PassphraseTextfield(has_user_passphrase);
650  user_passphrase_textfield_->SetController(this);
651  user_passphrase_textfield_->SetEnabled(user_passphrase_ui_data_.IsEditable());
652  layout->AddView(user_passphrase_textfield_);
653  layout->AddView(new ControlledSettingIndicatorView(user_passphrase_ui_data_));
654  layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing);
655
656  // OTP label and input.
657  if (enable_otp_) {
658    layout->StartRow(0, column_view_set_id);
659    otp_label_ = new views::Label(l10n_util::GetStringUTF16(
660        IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_VPN_OTP));
661    layout->AddView(otp_label_);
662    otp_textfield_ = new views::Textfield(views::Textfield::STYLE_DEFAULT);
663    otp_textfield_->SetController(this);
664    layout->AddView(otp_textfield_);
665    layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing);
666  } else {
667    otp_label_ = NULL;
668    otp_textfield_ = NULL;
669  }
670
671  // Group Name label and input.
672  if (enable_group_name_) {
673    layout->StartRow(0, column_view_set_id);
674    group_name_label_ = new views::Label(l10n_util::GetStringUTF16(
675        IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_VPN_GROUP_NAME));
676    layout->AddView(group_name_label_);
677    group_name_textfield_ =
678        new views::Textfield(views::Textfield::STYLE_DEFAULT);
679    group_name_textfield_->SetController(this);
680    if (vpn && !vpn->group_name().empty())
681      group_name_textfield_->SetText(UTF8ToUTF16(vpn->group_name()));
682    layout->AddView(group_name_textfield_);
683    layout->AddView(new ControlledSettingIndicatorView(group_name_ui_data_));
684    layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing);
685  } else {
686    group_name_label_ = NULL;
687    group_name_textfield_ = NULL;
688  }
689
690  // Save credentials
691  layout->StartRow(0, column_view_set_id);
692  save_credentials_checkbox_ = new views::Checkbox(
693      l10n_util::GetStringUTF16(
694          IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_SAVE_CREDENTIALS));
695  save_credentials_checkbox_->SetEnabled(
696      save_credentials_ui_data_.IsEditable());
697  bool save_credentials = vpn ? vpn->save_credentials() : false;
698  save_credentials_checkbox_->SetChecked(save_credentials);
699  layout->SkipColumns(1);
700  layout->AddView(save_credentials_checkbox_);
701  layout->AddView(
702      new ControlledSettingIndicatorView(save_credentials_ui_data_));
703
704  // Error label.
705  layout->StartRow(0, column_view_set_id);
706  layout->SkipColumns(1);
707  error_label_ = new views::Label();
708  error_label_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
709  error_label_->SetEnabledColor(SK_ColorRED);
710  layout->AddView(error_label_);
711
712  // Set or hide the UI, update comboboxes and error labels.
713  Refresh();
714}
715
716void VPNConfigView::Refresh() {
717  NetworkLibrary* cros = NetworkLibrary::Get();
718
719  UpdateControls();
720
721  // Set certificate combo boxes.
722  VirtualNetwork* vpn = cros->FindVirtualNetworkByPath(service_path_);
723  if (server_ca_cert_combobox_) {
724    server_ca_cert_combobox_->ModelChanged();
725    if (enable_server_ca_cert_ &&
726        (vpn && !vpn->ca_cert_pem().empty())) {
727      // Select the current server CA certificate in the combobox.
728      int cert_index = CertLibrary::Get()->GetCertIndexByPEM(
729          CertLibrary::CERT_TYPE_SERVER_CA, vpn->ca_cert_pem());
730      if (cert_index >= 0) {
731        // Skip item for "Default"
732        server_ca_cert_combobox_->SetSelectedIndex(1 + cert_index);
733      } else {
734        server_ca_cert_combobox_->SetSelectedIndex(0);
735      }
736    } else {
737      server_ca_cert_combobox_->SetSelectedIndex(0);
738    }
739  }
740
741  if (user_cert_combobox_) {
742    user_cert_combobox_->ModelChanged();
743    if (enable_user_cert_ &&
744        (vpn && !vpn->client_cert_id().empty())) {
745      int cert_index = CertLibrary::Get()->GetCertIndexByPkcs11Id(
746          CertLibrary::CERT_TYPE_USER, vpn->client_cert_id());
747      if (cert_index >= 0)
748        user_cert_combobox_->SetSelectedIndex(cert_index);
749      else
750        user_cert_combobox_->SetSelectedIndex(0);
751    } else {
752      user_cert_combobox_->SetSelectedIndex(0);
753    }
754  }
755
756  UpdateErrorLabel();
757}
758
759void VPNConfigView::UpdateControlsToEnable() {
760  // Set which controls are enabled.
761  enable_psk_passphrase_ = false;
762  enable_user_cert_ = false;
763  enable_server_ca_cert_ = false;
764  enable_otp_ = false;
765  enable_group_name_ = false;
766  switch (provider_type_) {
767    case PROVIDER_TYPE_L2TP_IPSEC_PSK:
768      enable_psk_passphrase_ = true;
769      enable_group_name_ = true;
770      break;
771    case PROVIDER_TYPE_L2TP_IPSEC_USER_CERT:
772      enable_server_ca_cert_ = true;
773      enable_user_cert_ = HaveUserCerts();
774      enable_group_name_ = true;
775      break;
776    case PROVIDER_TYPE_OPEN_VPN:
777      enable_server_ca_cert_ = true;
778      enable_user_cert_ = HaveUserCerts();
779      enable_otp_ = true;
780      break;
781    default:
782      NOTREACHED();
783      break;
784  }
785}
786
787void VPNConfigView::UpdateControls() {
788  UpdateControlsToEnable();
789
790  if (psk_passphrase_label_)
791    psk_passphrase_label_->SetEnabled(enable_psk_passphrase_);
792  if (psk_passphrase_textfield_)
793    psk_passphrase_textfield_->SetEnabled(enable_psk_passphrase_ &&
794                                          psk_passphrase_ui_data_.IsEditable());
795
796  if (user_cert_label_)
797    user_cert_label_->SetEnabled(enable_user_cert_);
798  if (user_cert_combobox_)
799    user_cert_combobox_->SetEnabled(enable_user_cert_ &&
800                                    user_cert_ui_data_.IsEditable());
801
802  if (server_ca_cert_label_)
803    server_ca_cert_label_->SetEnabled(enable_server_ca_cert_);
804  if (server_ca_cert_combobox_)
805    server_ca_cert_combobox_->SetEnabled(enable_server_ca_cert_ &&
806                                         ca_cert_ui_data_.IsEditable());
807
808  if (otp_label_)
809    otp_label_->SetEnabled(enable_otp_);
810  if (otp_textfield_)
811    otp_textfield_->SetEnabled(enable_otp_);
812
813  if (group_name_label_)
814    group_name_label_->SetEnabled(enable_group_name_);
815  if (group_name_textfield_)
816    group_name_textfield_->SetEnabled(enable_group_name_ &&
817                                      group_name_ui_data_.IsEditable());
818}
819
820void VPNConfigView::UpdateErrorLabel() {
821  NetworkLibrary* cros = NetworkLibrary::Get();
822
823  // Error message.
824  std::string error_msg;
825  if (UserCertRequired() && CertLibrary::Get()->CertificatesLoaded()) {
826    if (!HaveUserCerts()) {
827      error_msg = l10n_util::GetStringUTF8(
828          IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_PLEASE_INSTALL_USER_CERT);
829    } else if (!IsUserCertValid()) {
830      error_msg = l10n_util::GetStringUTF8(
831          IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_REQUIRE_HARDWARE_BACKED);
832    }
833  }
834  if (error_msg.empty() && !service_path_.empty()) {
835    // TODO(kuan): differentiate between bad psk and user passphrases.
836    VirtualNetwork* vpn = cros->FindVirtualNetworkByPath(service_path_);
837    if (vpn && vpn->failed()) {
838      if (vpn->error() == ERROR_BAD_PASSPHRASE) {
839        error_msg = l10n_util::GetStringUTF8(
840            IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_BAD_PASSPHRASE);
841      } else {
842        error_msg = vpn->GetErrorString();
843      }
844    }
845  }
846  if (!error_msg.empty()) {
847    error_label_->SetText(UTF8ToUTF16(error_msg));
848    error_label_->SetVisible(true);
849  } else {
850    error_label_->SetVisible(false);
851  }
852}
853
854void VPNConfigView::UpdateCanLogin() {
855  parent_->GetDialogClientView()->UpdateDialogButtons();
856}
857
858bool VPNConfigView::UserCertRequired() const {
859  return provider_type_ == PROVIDER_TYPE_L2TP_IPSEC_USER_CERT;
860}
861
862bool VPNConfigView::HaveUserCerts() const {
863  return CertLibrary::Get()->NumCertificates(CertLibrary::CERT_TYPE_USER) > 0;
864}
865
866bool VPNConfigView::IsUserCertValid() const {
867  if (!user_cert_combobox_ || !enable_user_cert_)
868    return false;
869  int index = user_cert_combobox_->selected_index();
870  if (index < 0)
871    return false;
872  // Currently only hardware-backed user certificates are valid.
873  if (CertLibrary::Get()->IsHardwareBacked() &&
874      !CertLibrary::Get()->IsCertHardwareBackedAt(
875          CertLibrary::CERT_TYPE_USER, index))
876    return false;
877  return true;
878}
879
880const std::string VPNConfigView::GetTextFromField(views::Textfield* textfield,
881                                                  bool trim_whitespace) const {
882  if (!textfield)
883    return std::string();
884  std::string untrimmed = UTF16ToUTF8(textfield->text());
885  if (!trim_whitespace)
886    return untrimmed;
887  std::string result;
888  TrimWhitespaceASCII(untrimmed, TRIM_ALL, &result);
889  return result;
890}
891
892const std::string VPNConfigView::GetPassphraseFromField(
893    PassphraseTextfield* textfield) const {
894  if (!textfield)
895    return std::string();
896  return textfield->GetPassphrase();
897}
898
899void VPNConfigView::ParseVPNUIProperty(NetworkPropertyUIData* property_ui_data,
900                                       Network* network,
901                                       const std::string& dict_key,
902                                       const std::string& key) {
903  onc::ONCSource onc_source = onc::ONC_SOURCE_NONE;
904  const base::DictionaryValue* onc =
905      NetworkConfigView::FindPolicyForActiveUser(network, &onc_source);
906
907  VLOG_IF(1, !onc) << "No ONC found for VPN network " << network->unique_id();
908  property_ui_data->ParseOncProperty(
909      onc_source,
910      onc,
911      base::StringPrintf("%s.%s.%s",
912                         onc::network_config::kVPN,
913                         dict_key.c_str(),
914                         key.c_str()));
915}
916
917}  // namespace chromeos
918