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
25 namespace {
26
ProviderTypeToString(chromeos::VirtualNetwork::ProviderType type)27 string16 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
47 namespace chromeos {
48
GetItemCount()49 int 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
GetItemAt(int index)55 string16 VPNConfigView::ProviderTypeComboboxModel::GetItemAt(int index) {
56 VirtualNetwork::ProviderType type =
57 static_cast<VirtualNetwork::ProviderType>(index);
58 return ProviderTypeToString(type);
59 }
60
UserCertComboboxModel()61 VPNConfigView::UserCertComboboxModel::UserCertComboboxModel() {
62 // TODO(jamescook): populate user_certs_. chromium-os:14111
63 }
64
GetItemCount()65 int VPNConfigView::UserCertComboboxModel::GetItemCount() {
66 return static_cast<int>(user_certs_.size());
67 }
68
GetItemAt(int index)69 string16 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
VPNConfigView(NetworkConfigView * parent,VirtualNetwork * vpn)75 VPNConfigView::VPNConfigView(NetworkConfigView* parent, VirtualNetwork* vpn)
76 : ChildNetworkConfigView(parent, vpn) {
77 Init(vpn);
78 }
79
VPNConfigView(NetworkConfigView * parent)80 VPNConfigView::VPNConfigView(NetworkConfigView* parent)
81 : ChildNetworkConfigView(parent) {
82 Init(NULL);
83 }
84
~VPNConfigView()85 VPNConfigView::~VPNConfigView() {
86 }
87
UpdateCanLogin()88 void VPNConfigView::UpdateCanLogin() {
89 parent_->GetDialogClientView()->UpdateDialogButtons();
90 }
91
GetTitle()92 string16 VPNConfigView::GetTitle() {
93 return l10n_util::GetStringUTF16(IDS_OPTIONS_SETTINGS_ADD_VPN);
94 }
95
CanLogin()96 bool 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
UpdateErrorLabel()111 void 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
ContentsChanged(views::Textfield * sender,const string16 & new_contents)134 void 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
HandleKeyEvent(views::Textfield * sender,const views::KeyEvent & key_event)153 bool 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
ButtonPressed(views::Button * sender,const views::Event & event)163 void VPNConfigView::ButtonPressed(views::Button* sender,
164 const views::Event& event) {
165 }
166
ItemChanged(views::Combobox * combo_box,int prev_index,int new_index)167 void 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
Login()182 bool 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
Cancel()233 void VPNConfigView::Cancel() {
234 }
235
InitFocus()236 void VPNConfigView::InitFocus() {
237 // TODO(jamescook): Put focus in a more reasonable widget.
238 }
239
GetTextFromField(views::Textfield * textfield,bool trim_whitespace) const240 const 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
GetService() const250 const std::string VPNConfigView::GetService() const {
251 if (service_textfield_ != NULL)
252 return GetTextFromField(service_textfield_, true);
253 return service_path_;
254 }
255
GetServer() const256 const std::string VPNConfigView::GetServer() const {
257 if (server_textfield_ != NULL)
258 return GetTextFromField(server_textfield_, true);
259 return server_hostname_;
260 }
261
GetPSKPassphrase() const262 const 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
GetUsername() const269 const std::string VPNConfigView::GetUsername() const {
270 return GetTextFromField(username_textfield_, true);
271 }
272
GetUserPassphrase() const273 const std::string VPNConfigView::GetUserPassphrase() const {
274 return GetTextFromField(user_passphrase_textfield_, false);
275 }
276
Init(VirtualNetwork * vpn)277 void 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
EnableControls()427 void 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