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/chromeos/options/vpn_config_view.h"
6
7#include "base/string_util.h"
8#include "base/utf_string_conversions.h"
9#include "chrome/browser/chromeos/cros/cros_library.h"
10#include "chrome/browser/chromeos/login/user_manager.h"
11#include "grit/chromium_strings.h"
12#include "grit/generated_resources.h"
13#include "grit/locale_settings.h"
14#include "grit/theme_resources.h"
15#include "ui/base/l10n/l10n_util.h"
16#include "ui/base/resource/resource_bundle.h"
17#include "views/controls/button/image_button.h"
18#include "views/controls/button/native_button.h"
19#include "views/controls/label.h"
20#include "views/controls/textfield/textfield.h"
21#include "views/layout/grid_layout.h"
22#include "views/layout/layout_constants.h"
23#include "views/window/window.h"
24
25namespace {
26
27string16 ProviderTypeToString(chromeos::VirtualNetwork::ProviderType type) {
28  switch (type) {
29    case chromeos::VirtualNetwork::PROVIDER_TYPE_L2TP_IPSEC_PSK:
30      return l10n_util::GetStringUTF16(
31          IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_L2TP_IPSEC_PSK);
32    case chromeos::VirtualNetwork::PROVIDER_TYPE_L2TP_IPSEC_USER_CERT:
33      return l10n_util::GetStringUTF16(
34          IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_L2TP_IPSEC_USER_CERT);
35    case chromeos::VirtualNetwork::PROVIDER_TYPE_OPEN_VPN:
36      return l10n_util::GetStringUTF16(
37          IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_OPEN_VPN);
38    case chromeos::VirtualNetwork::PROVIDER_TYPE_MAX:
39      break;
40  }
41  NOTREACHED();
42  return string16();
43}
44
45}  // namespace
46
47namespace chromeos {
48
49int VPNConfigView::ProviderTypeComboboxModel::GetItemCount() {
50  // TODO(stevenjb): Include OpenVPN option once enabled.
51  return VirtualNetwork::PROVIDER_TYPE_L2TP_IPSEC_USER_CERT + 1;
52  // return VirtualNetwork::PROVIDER_TYPE_MAX;
53}
54
55string16 VPNConfigView::ProviderTypeComboboxModel::GetItemAt(int index) {
56  VirtualNetwork::ProviderType type =
57      static_cast<VirtualNetwork::ProviderType>(index);
58  return ProviderTypeToString(type);
59}
60
61VPNConfigView::UserCertComboboxModel::UserCertComboboxModel() {
62  // TODO(jamescook): populate user_certs_. chromium-os:14111
63}
64
65int VPNConfigView::UserCertComboboxModel::GetItemCount() {
66  return static_cast<int>(user_certs_.size());
67}
68
69string16 VPNConfigView::UserCertComboboxModel::GetItemAt(int index) {
70  if (index >= 0 && index < static_cast<int>(user_certs_.size()))
71    return ASCIIToUTF16(user_certs_[index]);
72  return string16();
73}
74
75VPNConfigView::VPNConfigView(NetworkConfigView* parent, VirtualNetwork* vpn)
76    : ChildNetworkConfigView(parent, vpn) {
77  Init(vpn);
78}
79
80VPNConfigView::VPNConfigView(NetworkConfigView* parent)
81    : ChildNetworkConfigView(parent) {
82  Init(NULL);
83}
84
85VPNConfigView::~VPNConfigView() {
86}
87
88void VPNConfigView::UpdateCanLogin() {
89  parent_->GetDialogClientView()->UpdateDialogButtons();
90}
91
92string16 VPNConfigView::GetTitle() {
93  return l10n_util::GetStringUTF16(IDS_OPTIONS_SETTINGS_ADD_VPN);
94}
95
96bool VPNConfigView::CanLogin() {
97  static const size_t kMinPassphraseLen = 0;  // TODO(stevenjb): min length?
98  if (service_path_.empty() &&
99      (GetService().empty() || GetServer().empty()))
100    return false;
101  if (provider_type_ == VirtualNetwork::PROVIDER_TYPE_L2TP_IPSEC_PSK &&
102      psk_passphrase_textfield_->text().length() < kMinPassphraseLen)
103    return false;
104  if (GetUsername().empty())
105    return false;
106  if (user_passphrase_textfield_->text().length() < kMinPassphraseLen)
107    return false;
108  return true;
109}
110
111void VPNConfigView::UpdateErrorLabel() {
112  std::string error_msg;
113  if (!service_path_.empty()) {
114    // TODO(kuan): differentiate between bad psk and user passphrases.
115    NetworkLibrary* cros = CrosLibrary::Get()->GetNetworkLibrary();
116    VirtualNetwork* vpn = cros->FindVirtualNetworkByPath(service_path_);
117    if (vpn && vpn->failed()) {
118      if (vpn->error() == ERROR_BAD_PASSPHRASE) {
119        error_msg = l10n_util::GetStringUTF8(
120            IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_BAD_PASSPHRASE);
121      } else {
122        error_msg = vpn->GetErrorString();
123      }
124    }
125  }
126  if (!error_msg.empty()) {
127    error_label_->SetText(UTF8ToWide(error_msg));
128    error_label_->SetVisible(true);
129  } else {
130    error_label_->SetVisible(false);
131  }
132}
133
134void VPNConfigView::ContentsChanged(views::Textfield* sender,
135                                    const string16& new_contents) {
136  if (sender == server_textfield_ && !service_text_modified_) {
137    // Set the service name to the server name up to '.', unless it has
138    // been explicityly set by the user.
139    string16 server = server_textfield_->text();
140    string16::size_type n = server.find_first_of(L'.');
141    service_name_from_server_ = server.substr(0, n);
142    service_textfield_->SetText(service_name_from_server_);
143  }
144  if (sender == service_textfield_) {
145    if (new_contents.empty())
146      service_text_modified_ = false;
147    else if (new_contents != service_name_from_server_)
148      service_text_modified_ = true;
149  }
150  UpdateCanLogin();
151}
152
153bool VPNConfigView::HandleKeyEvent(views::Textfield* sender,
154                                   const views::KeyEvent& key_event) {
155  if ((sender == psk_passphrase_textfield_ ||
156       sender == user_passphrase_textfield_) &&
157      key_event.key_code() == ui::VKEY_RETURN) {
158    parent_->GetDialogClientView()->AcceptWindow();
159  }
160  return false;
161}
162
163void VPNConfigView::ButtonPressed(views::Button* sender,
164                                  const views::Event& event) {
165}
166
167void VPNConfigView::ItemChanged(views::Combobox* combo_box,
168                                int prev_index, int new_index) {
169  if (prev_index == new_index)
170    return;
171  if (combo_box == provider_type_combobox_) {
172    provider_type_ = static_cast<VirtualNetwork::ProviderType>(new_index);
173    EnableControls();
174  } else if (combo_box == user_cert_combobox_) {
175    // Nothing to do for now.
176  } else {
177    NOTREACHED();
178  }
179  UpdateCanLogin();
180}
181
182bool VPNConfigView::Login() {
183  NetworkLibrary* cros = CrosLibrary::Get()->GetNetworkLibrary();
184  if (service_path_.empty()) {
185    switch (provider_type_) {
186      case VirtualNetwork::PROVIDER_TYPE_L2TP_IPSEC_PSK:
187        cros->ConnectToVirtualNetworkPSK(GetService(),
188                                         GetServer(),
189                                         GetPSKPassphrase(),
190                                         GetUsername(),
191                                         GetUserPassphrase());
192        break;
193      case VirtualNetwork::PROVIDER_TYPE_L2TP_IPSEC_USER_CERT:
194      case VirtualNetwork::PROVIDER_TYPE_OPEN_VPN:
195        // TODO(stevenjb): Add support for OpenVPN and user certs.
196        LOG(WARNING) << "Unsupported provider type: " << provider_type_;
197        break;
198      case VirtualNetwork::PROVIDER_TYPE_MAX:
199        break;
200    }
201  } else {
202    VirtualNetwork* vpn = cros->FindVirtualNetworkByPath(service_path_);
203    if (!vpn) {
204      // TODO(stevenjb): Add notification for this.
205      LOG(WARNING) << "VPN no longer exists: " << service_path_;
206      return true;  // Close dialog.
207    }
208    switch (provider_type_) {
209      case VirtualNetwork::PROVIDER_TYPE_L2TP_IPSEC_PSK:
210        vpn->SetPSKPassphrase(GetPSKPassphrase());
211        break;
212      case VirtualNetwork::PROVIDER_TYPE_L2TP_IPSEC_USER_CERT:
213      case VirtualNetwork::PROVIDER_TYPE_OPEN_VPN: {
214        const std::string user_cert = UTF16ToUTF8(
215            user_cert_combobox_->model()->GetItemAt(
216                user_cert_combobox_->selected_item()));
217        vpn->SetUserCert(user_cert);
218        break;
219      }
220      case VirtualNetwork::PROVIDER_TYPE_MAX:
221        break;
222    }
223    vpn->SetUsername(GetUsername());
224    vpn->SetUserPassphrase(GetUserPassphrase());
225
226    cros->ConnectToVirtualNetwork(vpn);
227  }
228  // Connection failures are responsible for updating the UI, including
229  // reopening dialogs.
230  return true;  // Close dialog.
231}
232
233void VPNConfigView::Cancel() {
234}
235
236void VPNConfigView::InitFocus() {
237  // TODO(jamescook): Put focus in a more reasonable widget.
238}
239
240const std::string VPNConfigView::GetTextFromField(
241    views::Textfield* textfield, bool trim_whitespace) const {
242  std::string untrimmed = UTF16ToUTF8(textfield->text());
243  if (!trim_whitespace)
244    return untrimmed;
245  std::string result;
246  TrimWhitespaceASCII(untrimmed, TRIM_ALL, &result);
247  return result;
248}
249
250const std::string VPNConfigView::GetService() const {
251  if (service_textfield_ != NULL)
252    return GetTextFromField(service_textfield_, true);
253  return service_path_;
254}
255
256const std::string VPNConfigView::GetServer() const {
257  if (server_textfield_ != NULL)
258    return GetTextFromField(server_textfield_, true);
259  return server_hostname_;
260}
261
262const std::string VPNConfigView::GetPSKPassphrase() const {
263  if (psk_passphrase_textfield_->IsEnabled() &&
264      psk_passphrase_textfield_->IsVisible())
265    return GetTextFromField(psk_passphrase_textfield_, false);
266  return std::string();
267}
268
269const std::string VPNConfigView::GetUsername() const {
270  return GetTextFromField(username_textfield_, true);
271}
272
273const std::string VPNConfigView::GetUserPassphrase() const {
274  return GetTextFromField(user_passphrase_textfield_, false);
275}
276
277void VPNConfigView::Init(VirtualNetwork* vpn) {
278  views::GridLayout* layout = views::GridLayout::CreatePanel(this);
279  SetLayoutManager(layout);
280
281  int column_view_set_id = 0;
282  views::ColumnSet* column_set = layout->AddColumnSet(column_view_set_id);
283  // Label.
284  column_set->AddColumn(views::GridLayout::LEADING, views::GridLayout::FILL, 1,
285                        views::GridLayout::USE_PREF, 0, 0);
286  column_set->AddPaddingColumn(0, views::kRelatedControlSmallHorizontalSpacing);
287  // Textfield, combobox.
288  column_set->AddColumn(views::GridLayout::FILL, views::GridLayout::FILL, 1,
289                        views::GridLayout::USE_PREF, 0,
290                        ChildNetworkConfigView::kPassphraseWidth);
291  column_set->AddPaddingColumn(0, views::kRelatedControlSmallHorizontalSpacing);
292  // Passphrase visible button.
293  column_set->AddColumn(views::GridLayout::CENTER, views::GridLayout::FILL, 1,
294                        views::GridLayout::USE_PREF, 0, 0);
295
296  // Initialize members.
297  service_text_modified_ = false;
298  provider_type_ = vpn ?
299      vpn->provider_type() :
300      chromeos::VirtualNetwork::PROVIDER_TYPE_L2TP_IPSEC_PSK;
301
302  // Server label and input.
303  layout->StartRow(0, column_view_set_id);
304  layout->AddView(new views::Label(UTF16ToWide(l10n_util::GetStringUTF16(
305      IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_VPN_SERVER_HOSTNAME))));
306  if (!vpn) {
307    server_textfield_ = new views::Textfield(views::Textfield::STYLE_DEFAULT);
308    server_textfield_->SetController(this);
309    layout->AddView(server_textfield_);
310    server_text_ = NULL;
311  } else {
312    server_hostname_ = vpn->server_hostname();
313    server_text_ = new views::Label(UTF8ToWide(server_hostname_));
314    server_text_->SetHorizontalAlignment(views::Label::ALIGN_LEFT);
315    layout->AddView(server_text_);
316    server_textfield_ = NULL;
317  }
318  layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing);
319
320  // Service label and name or input.
321  layout->StartRow(0, column_view_set_id);
322  layout->AddView(new views::Label(UTF16ToWide(l10n_util::GetStringUTF16(
323      IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_VPN_SERVICE_NAME))));
324  if (!vpn) {
325    service_textfield_ = new views::Textfield(views::Textfield::STYLE_DEFAULT);
326    service_textfield_->SetController(this);
327    layout->AddView(service_textfield_);
328    service_text_ = NULL;
329  } else {
330    service_text_ = new views::Label(ASCIIToWide(vpn->name()));
331    service_text_->SetHorizontalAlignment(views::Label::ALIGN_LEFT);
332    layout->AddView(service_text_);
333    service_textfield_ = NULL;
334  }
335  layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing);
336
337  // Provider type label and select.
338  layout->StartRow(0, column_view_set_id);
339  layout->AddView(new views::Label(UTF16ToWide(l10n_util::GetStringUTF16(
340      IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_VPN_PROVIDER_TYPE))));
341  if (!vpn) {
342    provider_type_combobox_ =
343        new views::Combobox(new ProviderTypeComboboxModel());
344    provider_type_combobox_->set_listener(this);
345    layout->AddView(provider_type_combobox_);
346    provider_type_text_label_ = NULL;
347  } else {
348    provider_type_text_label_ =
349        new views::Label(UTF16ToWide(ProviderTypeToString(provider_type_)));
350    layout->AddView(provider_type_text_label_);
351    provider_type_combobox_ = NULL;
352  }
353  layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing);
354
355  // PSK passphrase label, input and visible button.
356  layout->StartRow(0, column_view_set_id);
357  psk_passphrase_label_ =  new views::Label(UTF16ToWide(
358      l10n_util::GetStringUTF16(
359          IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_VPN_PSK_PASSPHRASE)));
360  layout->AddView(psk_passphrase_label_);
361  psk_passphrase_textfield_ = new views::Textfield(
362      views::Textfield::STYLE_PASSWORD);
363  psk_passphrase_textfield_->SetController(this);
364  if (vpn && !vpn->psk_passphrase().empty())
365    psk_passphrase_textfield_->SetText(UTF8ToUTF16(vpn->psk_passphrase()));
366  layout->AddView(psk_passphrase_textfield_);
367  layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing);
368
369  // User certificate label and input.
370  layout->StartRow(0, column_view_set_id);
371  user_cert_label_ = new views::Label(UTF16ToWide(l10n_util::GetStringUTF16(
372      IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_VPN_USER_CERT)));
373  layout->AddView(user_cert_label_);
374  user_cert_combobox_ = new views::Combobox(new UserCertComboboxModel());
375  user_cert_combobox_->set_listener(this);
376  if (vpn && !vpn->user_cert().empty()) {
377    string16 user_cert = UTF8ToUTF16(vpn->user_cert());
378    for (int i = 0; i < user_cert_combobox_->model()->GetItemCount(); ++i) {
379      if (user_cert_combobox_->model()->GetItemAt(i) == user_cert) {
380        user_cert_combobox_->SetSelectedItem(i);
381        break;
382      }
383    }
384  }
385  layout->AddView(user_cert_combobox_);
386  layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing);
387
388  // Username label and input.
389  layout->StartRow(0, column_view_set_id);
390  layout->AddView(new views::Label(UTF16ToWide(l10n_util::GetStringUTF16(
391      IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_VPN_USERNAME))));
392  username_textfield_ = new views::Textfield(views::Textfield::STYLE_DEFAULT);
393  username_textfield_->SetController(this);
394  if (vpn && !vpn->username().empty())
395    username_textfield_->SetText(UTF8ToUTF16(vpn->username()));
396  layout->AddView(username_textfield_);
397  layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing);
398
399  // User passphrase label, input and visble button.
400  layout->StartRow(0, column_view_set_id);
401  layout->AddView(new views::Label(UTF16ToWide(
402      l10n_util::GetStringUTF16(
403          IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_VPN_USER_PASSPHRASE))));
404  user_passphrase_textfield_ = new views::Textfield(
405      views::Textfield::STYLE_PASSWORD);
406  user_passphrase_textfield_->SetController(this);
407  if (vpn && !vpn->user_passphrase().empty())
408    user_passphrase_textfield_->SetText(UTF8ToUTF16(vpn->user_passphrase()));
409  layout->AddView(user_passphrase_textfield_);
410  layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing);
411
412  // Error label.
413  layout->StartRow(0, column_view_set_id);
414  layout->SkipColumns(1);
415  error_label_ = new views::Label();
416  error_label_->SetHorizontalAlignment(views::Label::ALIGN_LEFT);
417  error_label_->SetColor(SK_ColorRED);
418  layout->AddView(error_label_);
419
420  // Enable controls based on provider type combo.
421  EnableControls();
422
423  // Set or hide the error text.
424  UpdateErrorLabel();
425}
426
427void VPNConfigView::EnableControls() {
428  switch (provider_type_) {
429    case VirtualNetwork::PROVIDER_TYPE_L2TP_IPSEC_PSK:
430      psk_passphrase_label_->SetEnabled(true);
431      psk_passphrase_textfield_->SetEnabled(true);
432      user_cert_label_->SetEnabled(false);
433      user_cert_combobox_->SetEnabled(false);
434      break;
435    case VirtualNetwork::PROVIDER_TYPE_L2TP_IPSEC_USER_CERT:
436    case VirtualNetwork::PROVIDER_TYPE_OPEN_VPN:
437      psk_passphrase_label_->SetEnabled(false);
438      psk_passphrase_textfield_->SetEnabled(false);
439      user_cert_label_->SetEnabled(true);
440      user_cert_combobox_->SetEnabled(true);
441      break;
442    default:
443      NOTREACHED();
444  }
445}
446
447}  // namespace chromeos
448