Add ICU message format support
[chromium-blink-merge.git] / extensions / browser / api / vpn_provider / vpn_service.cc
blob3afe0b7a6a0a9471105fbb5282db41e3ef37ed19
1 // Copyright 2014 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.
5 #include "extensions/browser/api/vpn_provider/vpn_service.h"
7 #include <stdint.h>
8 #include <vector>
10 #include "base/bind.h"
11 #include "base/bind_helpers.h"
12 #include "base/guid.h"
13 #include "base/location.h"
14 #include "base/logging.h"
15 #include "base/message_loop/message_loop.h"
16 #include "base/stl_util.h"
17 #include "base/strings/string_number_conversions.h"
18 #include "base/strings/string_util.h"
19 #include "base/values.h"
20 #include "chromeos/dbus/shill_third_party_vpn_driver_client.h"
21 #include "chromeos/dbus/shill_third_party_vpn_observer.h"
22 #include "chromeos/network/network_configuration_handler.h"
23 #include "chromeos/network/network_profile.h"
24 #include "chromeos/network/network_profile_handler.h"
25 #include "chromeos/network/network_state.h"
26 #include "chromeos/network/network_state_handler.h"
27 #include "chromeos/network/network_type_pattern.h"
28 #include "crypto/sha2.h"
29 #include "extensions/browser/event_router.h"
30 #include "extensions/browser/extension_registry.h"
31 #include "third_party/cros_system_api/dbus/service_constants.h"
33 namespace chromeos {
35 namespace {
37 namespace api_vpn = extensions::api::vpn_provider;
39 void DoNothingFailureCallback(const std::string& error_name,
40 const std::string& error_message) {
41 LOG(ERROR) << error_name << ": " << error_message;
44 } // namespace
46 class VpnService::VpnConfiguration : public ShillThirdPartyVpnObserver {
47 public:
48 VpnConfiguration(const std::string& extension_id,
49 const std::string& configuration_name,
50 const std::string& key,
51 base::WeakPtr<VpnService> vpn_service);
52 ~VpnConfiguration() override;
54 const std::string& extension_id() const { return extension_id_; }
55 const std::string& configuration_name() const { return configuration_name_; }
56 const std::string& key() const { return key_; }
57 const std::string& service_path() const { return service_path_; }
58 void set_service_path(const std::string& service_path) {
59 service_path_ = service_path;
61 const std::string& object_path() const { return object_path_; }
63 // ShillThirdPartyVpnObserver:
64 void OnPacketReceived(const std::vector<char>& data) override;
65 void OnPlatformMessage(uint32_t message) override;
67 private:
68 const std::string extension_id_;
69 const std::string configuration_name_;
70 const std::string key_;
71 const std::string object_path_;
73 std::string service_path_;
75 base::WeakPtr<VpnService> vpn_service_;
77 DISALLOW_COPY_AND_ASSIGN(VpnConfiguration);
80 VpnService::VpnConfiguration::VpnConfiguration(
81 const std::string& extension_id,
82 const std::string& configuration_name,
83 const std::string& key,
84 base::WeakPtr<VpnService> vpn_service)
85 : extension_id_(extension_id),
86 configuration_name_(configuration_name),
87 key_(key),
88 object_path_(shill::kObjectPathBase + key_),
89 vpn_service_(vpn_service) {
92 VpnService::VpnConfiguration::~VpnConfiguration() {
95 void VpnService::VpnConfiguration::OnPacketReceived(
96 const std::vector<char>& data) {
97 if (!vpn_service_) {
98 return;
100 scoped_ptr<base::ListValue> event_args =
101 api_vpn::OnPacketReceived::Create(data);
102 vpn_service_->SendSignalToExtension(
103 extension_id_, extensions::events::VPN_PROVIDER_ON_PACKET_RECEIVED,
104 api_vpn::OnPacketReceived::kEventName, event_args.Pass());
107 void VpnService::VpnConfiguration::OnPlatformMessage(uint32_t message) {
108 if (!vpn_service_) {
109 return;
111 DCHECK_GE(api_vpn::PLATFORM_MESSAGE_LAST, message);
113 api_vpn::PlatformMessage platform_message =
114 static_cast<api_vpn::PlatformMessage>(message);
115 vpn_service_->SetActiveConfiguration(
116 platform_message == api_vpn::PLATFORM_MESSAGE_CONNECTED ? this : nullptr);
118 // TODO(kaliamoorthi): Update the lower layers to get the error message and
119 // pass in the error instead of std::string().
120 scoped_ptr<base::ListValue> event_args = api_vpn::OnPlatformMessage::Create(
121 configuration_name_, platform_message, std::string());
123 vpn_service_->SendSignalToExtension(
124 extension_id_, extensions::events::VPN_PROVIDER_ON_PLATFORM_MESSAGE,
125 api_vpn::OnPlatformMessage::kEventName, event_args.Pass());
128 VpnService::VpnService(
129 content::BrowserContext* browser_context,
130 const std::string& userid_hash,
131 extensions::ExtensionRegistry* extension_registry,
132 extensions::EventRouter* event_router,
133 ShillThirdPartyVpnDriverClient* shill_client,
134 NetworkConfigurationHandler* network_configuration_handler,
135 NetworkProfileHandler* network_profile_handler,
136 NetworkStateHandler* network_state_handler)
137 : browser_context_(browser_context),
138 userid_hash_(userid_hash),
139 extension_registry_(extension_registry),
140 event_router_(event_router),
141 shill_client_(shill_client),
142 network_configuration_handler_(network_configuration_handler),
143 network_profile_handler_(network_profile_handler),
144 network_state_handler_(network_state_handler),
145 active_configuration_(nullptr),
146 weak_factory_(this) {
147 extension_registry_->AddObserver(this);
148 network_state_handler_->AddObserver(this, FROM_HERE);
149 network_configuration_handler_->AddObserver(this);
150 base::MessageLoop::current()->PostTask(
151 FROM_HERE,
152 base::Bind(&VpnService::NetworkListChanged, weak_factory_.GetWeakPtr()));
155 VpnService::~VpnService() {
156 network_configuration_handler_->RemoveObserver(this);
157 network_state_handler_->RemoveObserver(this, FROM_HERE);
158 extension_registry_->RemoveObserver(this);
159 STLDeleteContainerPairSecondPointers(key_to_configuration_map_.begin(),
160 key_to_configuration_map_.end());
163 void VpnService::SendShowAddDialogToExtension(const std::string& extension_id) {
164 SendSignalToExtension(extension_id,
165 extensions::events::VPN_PROVIDER_ON_UI_EVENT,
166 api_vpn::OnUIEvent::kEventName,
167 api_vpn::OnUIEvent::Create(
168 api_vpn::UI_EVENT_SHOWADDDIALOG, std::string()));
171 void VpnService::SendShowConfigureDialogToExtension(
172 const std::string& extension_id,
173 const std::string& configuration_id) {
174 SendSignalToExtension(
175 extension_id, extensions::events::VPN_PROVIDER_ON_UI_EVENT,
176 api_vpn::OnUIEvent::kEventName,
177 api_vpn::OnUIEvent::Create(api_vpn::UI_EVENT_SHOWCONFIGUREDIALOG,
178 configuration_id));
181 void VpnService::SendPlatformError(const std::string& extension_id,
182 const std::string& configuration_id,
183 const std::string& error_message) {
184 SendSignalToExtension(
185 extension_id, extensions::events::VPN_PROVIDER_ON_PLATFORM_MESSAGE,
186 api_vpn::OnPlatformMessage::kEventName,
187 api_vpn::OnPlatformMessage::Create(
188 configuration_id, api_vpn::PLATFORM_MESSAGE_ERROR, error_message));
191 std::string VpnService::GetKey(const std::string& extension_id,
192 const std::string& name) {
193 const std::string key = crypto::SHA256HashString(extension_id + name);
194 return base::HexEncode(key.data(), key.size());
197 void VpnService::OnConfigurationCreated(const std::string& service_path,
198 const std::string& profile_path,
199 const base::DictionaryValue& properties,
200 Source source) {
203 void VpnService::OnConfigurationRemoved(const std::string& service_path,
204 const std::string& guid,
205 Source source) {
206 if (source == SOURCE_EXTENSION_INSTALL) {
207 // No need to process if the configuration was removed using an extension
208 // API since the API would have already done the cleanup.
209 return;
212 if (service_path_to_configuration_map_.find(service_path) ==
213 service_path_to_configuration_map_.end()) {
214 // Ignore removal of a configuration unknown to VPN service, which means the
215 // configuration was created internally by the platform.
216 return;
219 VpnConfiguration* configuration =
220 service_path_to_configuration_map_[service_path];
221 scoped_ptr<base::ListValue> event_args =
222 api_vpn::OnConfigRemoved::Create(configuration->configuration_name());
223 SendSignalToExtension(configuration->extension_id(),
224 extensions::events::VPN_PROVIDER_ON_CONFIG_REMOVED,
225 api_vpn::OnConfigRemoved::kEventName,
226 event_args.Pass());
228 DestroyConfigurationInternal(configuration);
231 void VpnService::OnPropertiesSet(const std::string& service_path,
232 const std::string& guid,
233 const base::DictionaryValue& set_properties,
234 Source source) {
237 void VpnService::OnConfigurationProfileChanged(const std::string& service_path,
238 const std::string& profile_path,
239 Source source) {
242 void VpnService::OnGetPropertiesSuccess(
243 const std::string& service_path,
244 const base::DictionaryValue& dictionary) {
245 if (service_path_to_configuration_map_.find(service_path) !=
246 service_path_to_configuration_map_.end()) {
247 return;
249 std::string vpn_type;
250 std::string extension_id;
251 std::string type;
252 std::string configuration_name;
253 if (!dictionary.GetString(shill::kProviderTypeProperty, &vpn_type) ||
254 !dictionary.GetString(shill::kProviderHostProperty, &extension_id) ||
255 !dictionary.GetString(shill::kTypeProperty, &type) ||
256 !dictionary.GetString(shill::kNameProperty, &configuration_name) ||
257 vpn_type != shill::kProviderThirdPartyVpn || type != shill::kTypeVPN) {
258 return;
261 if (!extension_registry_->GetExtensionById(
262 extension_id, extensions::ExtensionRegistry::ENABLED)) {
263 // Does not belong to this instance of VpnService.
264 return;
267 const std::string key = GetKey(extension_id, configuration_name);
268 VpnConfiguration* configuration =
269 CreateConfigurationInternal(extension_id, configuration_name, key);
270 configuration->set_service_path(service_path);
271 service_path_to_configuration_map_[service_path] = configuration;
272 shill_client_->AddShillThirdPartyVpnObserver(configuration->object_path(),
273 configuration);
276 void VpnService::OnGetPropertiesFailure(
277 const std::string& error_name,
278 scoped_ptr<base::DictionaryValue> error_data) {
281 void VpnService::NetworkListChanged() {
282 NetworkStateHandler::NetworkStateList network_list;
283 network_state_handler_->GetVisibleNetworkListByType(NetworkTypePattern::VPN(),
284 &network_list);
285 for (auto& iter : network_list) {
286 if (service_path_to_configuration_map_.find(iter->path()) !=
287 service_path_to_configuration_map_.end()) {
288 continue;
291 network_configuration_handler_->GetShillProperties(
292 iter->path(), base::Bind(&VpnService::OnGetPropertiesSuccess,
293 weak_factory_.GetWeakPtr()),
294 base::Bind(&VpnService::OnGetPropertiesFailure,
295 weak_factory_.GetWeakPtr()));
299 void VpnService::CreateConfiguration(const std::string& extension_id,
300 const std::string& extension_name,
301 const std::string& configuration_name,
302 const SuccessCallback& success,
303 const FailureCallback& failure) {
304 if (configuration_name.empty()) {
305 failure.Run(std::string(), std::string("Empty name not supported."));
306 return;
309 const std::string key = GetKey(extension_id, configuration_name);
310 if (ContainsKey(key_to_configuration_map_, key)) {
311 failure.Run(std::string(), std::string("Name not unique."));
312 return;
315 const NetworkProfile* profile =
316 network_profile_handler_->GetProfileForUserhash(userid_hash_);
317 if (!profile) {
318 failure.Run(
319 std::string(),
320 std::string("No user profile for unshared network configuration."));
321 return;
324 VpnConfiguration* configuration =
325 CreateConfigurationInternal(extension_id, configuration_name, key);
327 base::DictionaryValue properties;
328 properties.SetStringWithoutPathExpansion(shill::kTypeProperty,
329 shill::kTypeVPN);
330 properties.SetStringWithoutPathExpansion(shill::kNameProperty,
331 configuration_name);
332 properties.SetStringWithoutPathExpansion(shill::kProviderHostProperty,
333 extension_id);
334 properties.SetStringWithoutPathExpansion(shill::kObjectPathSuffixProperty,
335 configuration->key());
336 properties.SetStringWithoutPathExpansion(shill::kProviderTypeProperty,
337 shill::kProviderThirdPartyVpn);
338 properties.SetStringWithoutPathExpansion(shill::kProfileProperty,
339 profile->path);
341 // Note: This will not create an entry in |policy_util|. TODO(pneubeck):
342 // Determine the correct thing to do here, crbug.com/459278.
343 std::string guid = base::GenerateGUID();
344 properties.SetStringWithoutPathExpansion(shill::kGuidProperty, guid);
346 network_configuration_handler_->CreateShillConfiguration(
347 properties, NetworkConfigurationObserver::SOURCE_EXTENSION_INSTALL,
348 base::Bind(&VpnService::OnCreateConfigurationSuccess,
349 weak_factory_.GetWeakPtr(), success, configuration),
350 base::Bind(&VpnService::OnCreateConfigurationFailure,
351 weak_factory_.GetWeakPtr(), failure, configuration));
354 void VpnService::DestroyConfiguration(const std::string& extension_id,
355 const std::string& configuration_id,
356 const SuccessCallback& success,
357 const FailureCallback& failure) {
358 // The ID is the configuration name for now. This may change in the future.
359 const std::string key = GetKey(extension_id, configuration_id);
360 if (!ContainsKey(key_to_configuration_map_, key)) {
361 failure.Run(std::string(), std::string("Unauthorized access."));
362 return;
365 VpnConfiguration* configuration = key_to_configuration_map_[key];
366 const std::string service_path = configuration->service_path();
367 if (service_path.empty()) {
368 failure.Run(std::string(), std::string("Pending create."));
369 return;
371 if (active_configuration_ == configuration) {
372 configuration->OnPlatformMessage(api_vpn::PLATFORM_MESSAGE_DISCONNECTED);
374 DestroyConfigurationInternal(configuration);
376 network_configuration_handler_->RemoveConfiguration(
377 service_path, NetworkConfigurationObserver::SOURCE_EXTENSION_INSTALL,
378 base::Bind(&VpnService::OnRemoveConfigurationSuccess,
379 weak_factory_.GetWeakPtr(), success),
380 base::Bind(&VpnService::OnRemoveConfigurationFailure,
381 weak_factory_.GetWeakPtr(), failure));
384 void VpnService::SetParameters(const std::string& extension_id,
385 const base::DictionaryValue& parameters,
386 const StringCallback& success,
387 const FailureCallback& failure) {
388 if (!DoesActiveConfigurationExistAndIsAccessAuthorized(extension_id)) {
389 failure.Run(std::string(), std::string("Unauthorized access."));
390 return;
393 shill_client_->SetParameters(active_configuration_->object_path(), parameters,
394 success, failure);
397 void VpnService::SendPacket(const std::string& extension_id,
398 const std::vector<char>& data,
399 const SuccessCallback& success,
400 const FailureCallback& failure) {
401 if (!DoesActiveConfigurationExistAndIsAccessAuthorized(extension_id)) {
402 failure.Run(std::string(), std::string("Unauthorized access."));
403 return;
406 if (data.empty()) {
407 failure.Run(std::string(), std::string("Can't send an empty packet."));
408 return;
411 shill_client_->SendPacket(active_configuration_->object_path(), data, success,
412 failure);
415 void VpnService::NotifyConnectionStateChanged(const std::string& extension_id,
416 api_vpn::VpnConnectionState state,
417 const SuccessCallback& success,
418 const FailureCallback& failure) {
419 if (!DoesActiveConfigurationExistAndIsAccessAuthorized(extension_id)) {
420 failure.Run(std::string(), std::string("Unauthorized access."));
421 return;
424 shill_client_->UpdateConnectionState(active_configuration_->object_path(),
425 static_cast<uint32_t>(state), success,
426 failure);
429 bool VpnService::VerifyConfigExistsForTesting(
430 const std::string& extension_id,
431 const std::string& configuration_name) {
432 const std::string key = GetKey(extension_id, configuration_name);
433 return ContainsKey(key_to_configuration_map_, key);
436 bool VpnService::VerifyConfigIsConnectedForTesting(
437 const std::string& extension_id) {
438 return DoesActiveConfigurationExistAndIsAccessAuthorized(extension_id);
441 void VpnService::DestroyConfigurationsForExtension(
442 const extensions::Extension* extension) {
443 std::vector<VpnConfiguration*> to_be_destroyed;
444 for (const auto& iter : key_to_configuration_map_) {
445 if (iter.second->extension_id() == extension->id()) {
446 to_be_destroyed.push_back(iter.second);
450 for (auto& iter : to_be_destroyed) {
451 DestroyConfiguration(extension->id(), // Extension ID
452 iter->configuration_name(), // Configuration name
453 base::Bind(base::DoNothing),
454 base::Bind(DoNothingFailureCallback));
458 void VpnService::OnExtensionUninstalled(
459 content::BrowserContext* browser_context,
460 const extensions::Extension* extension,
461 extensions::UninstallReason reason) {
462 if (browser_context != browser_context_) {
463 NOTREACHED();
464 return;
467 DestroyConfigurationsForExtension(extension);
470 void VpnService::OnExtensionUnloaded(
471 content::BrowserContext* browser_context,
472 const extensions::Extension* extension,
473 extensions::UnloadedExtensionInfo::Reason reason) {
474 if (browser_context != browser_context_) {
475 NOTREACHED();
476 return;
479 if (active_configuration_ &&
480 active_configuration_->extension_id() == extension->id()) {
481 shill_client_->UpdateConnectionState(
482 active_configuration_->object_path(),
483 static_cast<uint32_t>(api_vpn::VPN_CONNECTION_STATE_FAILURE),
484 base::Bind(base::DoNothing), base::Bind(DoNothingFailureCallback));
486 if (reason == extensions::UnloadedExtensionInfo::REASON_DISABLE ||
487 reason == extensions::UnloadedExtensionInfo::REASON_BLACKLIST) {
488 DestroyConfigurationsForExtension(extension);
492 void VpnService::OnCreateConfigurationSuccess(
493 const VpnService::SuccessCallback& callback,
494 VpnConfiguration* configuration,
495 const std::string& service_path) {
496 configuration->set_service_path(service_path);
497 service_path_to_configuration_map_[service_path] = configuration;
498 shill_client_->AddShillThirdPartyVpnObserver(configuration->object_path(),
499 configuration);
500 callback.Run();
503 void VpnService::OnCreateConfigurationFailure(
504 const VpnService::FailureCallback& callback,
505 VpnConfiguration* configuration,
506 const std::string& error_name,
507 scoped_ptr<base::DictionaryValue> error_data) {
508 DestroyConfigurationInternal(configuration);
509 callback.Run(error_name, std::string());
512 void VpnService::OnRemoveConfigurationSuccess(
513 const VpnService::SuccessCallback& callback) {
514 callback.Run();
517 void VpnService::OnRemoveConfigurationFailure(
518 const VpnService::FailureCallback& callback,
519 const std::string& error_name,
520 scoped_ptr<base::DictionaryValue> error_data) {
521 callback.Run(error_name, std::string());
524 void VpnService::SendSignalToExtension(
525 const std::string& extension_id,
526 extensions::events::HistogramValue histogram_value,
527 const std::string& event_name,
528 scoped_ptr<base::ListValue> event_args) {
529 scoped_ptr<extensions::Event> event(new extensions::Event(
530 histogram_value, event_name, event_args.Pass(), browser_context_));
532 event_router_->DispatchEventToExtension(extension_id, event.Pass());
535 void VpnService::SetActiveConfiguration(
536 VpnService::VpnConfiguration* configuration) {
537 active_configuration_ = configuration;
540 VpnService::VpnConfiguration* VpnService::CreateConfigurationInternal(
541 const std::string& extension_id,
542 const std::string& configuration_name,
543 const std::string& key) {
544 VpnConfiguration* configuration = new VpnConfiguration(
545 extension_id, configuration_name, key, weak_factory_.GetWeakPtr());
546 // The object is owned by key_to_configuration_map_ henceforth.
547 key_to_configuration_map_[key] = configuration;
548 return configuration;
551 void VpnService::DestroyConfigurationInternal(VpnConfiguration* configuration) {
552 key_to_configuration_map_.erase(configuration->key());
553 if (active_configuration_ == configuration) {
554 active_configuration_ = nullptr;
556 if (!configuration->service_path().empty()) {
557 shill_client_->RemoveShillThirdPartyVpnObserver(
558 configuration->object_path());
559 service_path_to_configuration_map_.erase(configuration->service_path());
561 delete configuration;
564 bool VpnService::DoesActiveConfigurationExistAndIsAccessAuthorized(
565 const std::string& extension_id) {
566 return active_configuration_ &&
567 active_configuration_->extension_id() == extension_id;
570 } // namespace chromeos