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 "chromeos/network/network_configuration_handler.h" 6 7#include <string> 8#include <vector> 9 10#include "base/bind.h" 11#include "base/format_macros.h" 12#include "base/json/json_writer.h" 13#include "base/logging.h" 14#include "base/memory/ref_counted.h" 15#include "base/memory/scoped_ptr.h" 16#include "base/stl_util.h" 17#include "base/strings/stringprintf.h" 18#include "base/values.h" 19#include "chromeos/dbus/dbus_thread_manager.h" 20#include "chromeos/dbus/shill_manager_client.h" 21#include "chromeos/dbus/shill_profile_client.h" 22#include "chromeos/dbus/shill_service_client.h" 23#include "chromeos/network/network_event_log.h" 24#include "chromeos/network/network_state.h" 25#include "chromeos/network/network_state_handler.h" 26#include "dbus/object_path.h" 27#include "third_party/cros_system_api/dbus/service_constants.h" 28 29namespace chromeos { 30 31namespace { 32 33// Strip surrounding "" from keys (if present). 34std::string StripQuotations(const std::string& in_str) { 35 size_t len = in_str.length(); 36 if (len >= 2 && in_str[0] == '"' && in_str[len-1] == '"') 37 return in_str.substr(1, len-2); 38 return in_str; 39} 40 41void InvokeErrorCallback(const std::string& error, 42 const std::string& path, 43 const network_handler::ErrorCallback& error_callback) { 44 NET_LOG_ERROR(error, path); 45 if (error_callback.is_null()) 46 return; 47 scoped_ptr<base::DictionaryValue> error_data( 48 network_handler::CreateErrorData(path, error, "")); 49 error_callback.Run(error, error_data.Pass()); 50} 51 52void GetPropertiesCallback( 53 const network_handler::DictionaryResultCallback& callback, 54 const network_handler::ErrorCallback& error_callback, 55 const std::string& service_path, 56 DBusMethodCallStatus call_status, 57 const base::DictionaryValue& properties) { 58 // Get the correct name from WifiHex if necessary. 59 scoped_ptr<base::DictionaryValue> properties_copy(properties.DeepCopy()); 60 std::string name = NetworkState::GetNameFromProperties( 61 service_path, properties); 62 if (!name.empty()) { 63 properties_copy->SetStringWithoutPathExpansion( 64 flimflam::kNameProperty, name); 65 } 66 if (call_status != DBUS_METHOD_CALL_SUCCESS) { 67 // Because network services are added and removed frequently, we will see 68 // failures regularly, so don't log these. 69 if (!error_callback.is_null()) { 70 scoped_ptr<base::DictionaryValue> error_data( 71 network_handler::CreateErrorData( 72 service_path, 73 network_handler::kDBusFailedError, 74 network_handler::kDBusFailedErrorMessage)); 75 error_callback.Run(network_handler::kDBusFailedError, error_data.Pass()); 76 } 77 } else if (!callback.is_null()) { 78 callback.Run(service_path, *properties_copy.get()); 79 } 80} 81 82void SetNetworkProfileErrorCallback( 83 const std::string& service_path, 84 const std::string& profile_path, 85 const network_handler::ErrorCallback& error_callback, 86 const std::string& dbus_error_name, 87 const std::string& dbus_error_message) { 88 network_handler::ShillErrorCallbackFunction( 89 "Config.SetNetworkProfile Failed: " + profile_path, 90 service_path, error_callback, 91 dbus_error_name, dbus_error_message); 92} 93 94bool IsPassphrase(const std::string& key) { 95 return key == flimflam::kEapPrivateKeyPasswordProperty || 96 key == flimflam::kEapPasswordProperty || 97 key == flimflam::kL2tpIpsecPasswordProperty || 98 key == flimflam::kOpenVPNPasswordProperty || 99 key == flimflam::kPassphraseProperty || 100 key == flimflam::kOpenVPNOTPProperty || 101 key == flimflam::kEapPrivateKeyProperty || 102 key == flimflam::kEapPrivateKeyPasswordProperty || 103 key == flimflam::kEapPinProperty || 104 key == flimflam::kApnPasswordProperty; 105} 106 107void LogConfigProperties(const std::string& desc, 108 const std::string& path, 109 const base::DictionaryValue& properties) { 110 for (base::DictionaryValue::Iterator iter(properties); 111 !iter.IsAtEnd(); iter.Advance()) { 112 std::string v = "******"; 113 if (!IsPassphrase(iter.key())) 114 base::JSONWriter::Write(&iter.value(), &v); 115 NET_LOG_DEBUG(desc, path + "." + iter.key() + "=" + v); 116 } 117} 118 119} // namespace 120 121// Helper class to request from Shill the profile entries associated with a 122// Service and delete the service from each profile. Triggers either 123// |callback| on success or |error_callback| on failure, and calls 124// |handler|->ProfileEntryDeleterCompleted() on completion to delete itself. 125class NetworkConfigurationHandler::ProfileEntryDeleter 126 : public base::SupportsWeakPtr<ProfileEntryDeleter> { 127 public: 128 ProfileEntryDeleter(NetworkConfigurationHandler* handler, 129 const std::string& service_path, 130 const base::Closure& callback, 131 const network_handler::ErrorCallback& error_callback) 132 : owner_(handler), 133 service_path_(service_path), 134 callback_(callback), 135 error_callback_(error_callback) { 136 } 137 138 void Run() { 139 DBusThreadManager::Get()->GetShillServiceClient()-> 140 GetLoadableProfileEntries( 141 dbus::ObjectPath(service_path_), 142 base::Bind(&ProfileEntryDeleter::GetProfileEntriesToDeleteCallback, 143 AsWeakPtr())); 144 } 145 146 private: 147 void GetProfileEntriesToDeleteCallback( 148 DBusMethodCallStatus call_status, 149 const base::DictionaryValue& profile_entries) { 150 if (call_status != DBUS_METHOD_CALL_SUCCESS) { 151 InvokeErrorCallback( 152 "GetLoadableProfileEntries Failed", service_path_, error_callback_); 153 owner_->ProfileEntryDeleterCompleted(service_path_); // Deletes this. 154 return; 155 } 156 157 for (base::DictionaryValue::Iterator iter(profile_entries); 158 !iter.IsAtEnd(); iter.Advance()) { 159 std::string profile_path = StripQuotations(iter.key()); 160 std::string entry_path; 161 iter.value().GetAsString(&entry_path); 162 if (profile_path.empty() || entry_path.empty()) { 163 NET_LOG_ERROR("Failed to parse Profile Entry", base::StringPrintf( 164 "%s: %s", profile_path.c_str(), entry_path.c_str())); 165 continue; 166 } 167 if (profile_delete_entries_.count(profile_path) != 0) { 168 NET_LOG_ERROR("Multiple Profile Entries", base::StringPrintf( 169 "%s: %s", profile_path.c_str(), entry_path.c_str())); 170 continue; 171 } 172 NET_LOG_DEBUG("Delete Profile Entry", base::StringPrintf( 173 "%s: %s", profile_path.c_str(), entry_path.c_str())); 174 profile_delete_entries_[profile_path] = entry_path; 175 DBusThreadManager::Get()->GetShillProfileClient()->DeleteEntry( 176 dbus::ObjectPath(profile_path), 177 entry_path, 178 base::Bind(&ProfileEntryDeleter::ProfileEntryDeletedCallback, 179 AsWeakPtr(), profile_path, entry_path), 180 base::Bind(&ProfileEntryDeleter::ShillErrorCallback, 181 AsWeakPtr(), profile_path, entry_path)); 182 } 183 } 184 185 void ProfileEntryDeletedCallback(const std::string& profile_path, 186 const std::string& entry) { 187 NET_LOG_DEBUG("Profile Entry Deleted", base::StringPrintf( 188 "%s: %s", profile_path.c_str(), entry.c_str())); 189 profile_delete_entries_.erase(profile_path); 190 if (!profile_delete_entries_.empty()) 191 return; 192 // Run the callback if this is the last pending deletion. 193 if (!callback_.is_null()) 194 callback_.Run(); 195 // Request NetworkStateHandler manager update to update ServiceCompleteList. 196 owner_->network_state_handler_->UpdateManagerProperties(); 197 owner_->ProfileEntryDeleterCompleted(service_path_); // Deletes this. 198 } 199 200 void ShillErrorCallback(const std::string& profile_path, 201 const std::string& entry, 202 const std::string& dbus_error_name, 203 const std::string& dbus_error_message) { 204 // Any Shill Error triggers a failure / error. 205 network_handler::ShillErrorCallbackFunction( 206 "GetLoadableProfileEntries Failed", profile_path, error_callback_, 207 dbus_error_name, dbus_error_message); 208 // Delete this even if there are pending deletions; any callbacks will 209 // safely become no-ops (by invalidating the WeakPtrs). 210 owner_->ProfileEntryDeleterCompleted(service_path_); // Deletes this. 211 } 212 213 NetworkConfigurationHandler* owner_; // Unowned 214 std::string service_path_; 215 base::Closure callback_; 216 network_handler::ErrorCallback error_callback_; 217 218 // Map of pending profile entry deletions, indexed by profile path. 219 std::map<std::string, std::string> profile_delete_entries_; 220 221 DISALLOW_COPY_AND_ASSIGN(ProfileEntryDeleter); 222}; 223 224// NetworkConfigurationHandler 225 226void NetworkConfigurationHandler::GetProperties( 227 const std::string& service_path, 228 const network_handler::DictionaryResultCallback& callback, 229 const network_handler::ErrorCallback& error_callback) const { 230 DBusThreadManager::Get()->GetShillServiceClient()->GetProperties( 231 dbus::ObjectPath(service_path), 232 base::Bind(&GetPropertiesCallback, 233 callback, error_callback, service_path)); 234} 235 236void NetworkConfigurationHandler::SetProperties( 237 const std::string& service_path, 238 const base::DictionaryValue& properties, 239 const base::Closure& callback, 240 const network_handler::ErrorCallback& error_callback) { 241 if (properties.empty()) { 242 if (!callback.is_null()) 243 callback.Run(); 244 return; 245 } 246 NET_LOG_USER("SetProperties", service_path); 247 LogConfigProperties("SetProperty", service_path, properties); 248 249 DBusThreadManager::Get()->GetShillServiceClient()->SetProperties( 250 dbus::ObjectPath(service_path), 251 properties, 252 base::Bind(&NetworkConfigurationHandler::SetPropertiesSuccessCallback, 253 AsWeakPtr(), service_path, callback), 254 base::Bind(&NetworkConfigurationHandler::SetPropertiesErrorCallback, 255 AsWeakPtr(), service_path, error_callback)); 256} 257 258void NetworkConfigurationHandler::ClearProperties( 259 const std::string& service_path, 260 const std::vector<std::string>& names, 261 const base::Closure& callback, 262 const network_handler::ErrorCallback& error_callback) { 263 if (names.empty()) { 264 if (!callback.is_null()) 265 callback.Run(); 266 return; 267 } 268 NET_LOG_USER("ClearProperties", service_path); 269 for (std::vector<std::string>::const_iterator iter = names.begin(); 270 iter != names.end(); ++iter) { 271 NET_LOG_DEBUG("ClearProperty", service_path + "." + *iter); 272 } 273 DBusThreadManager::Get()->GetShillServiceClient()->ClearProperties( 274 dbus::ObjectPath(service_path), 275 names, 276 base::Bind(&NetworkConfigurationHandler::ClearPropertiesSuccessCallback, 277 AsWeakPtr(), service_path, names, callback, error_callback), 278 base::Bind(&NetworkConfigurationHandler::ClearPropertiesErrorCallback, 279 AsWeakPtr(), service_path, error_callback)); 280} 281 282void NetworkConfigurationHandler::CreateConfiguration( 283 const base::DictionaryValue& properties, 284 const network_handler::StringResultCallback& callback, 285 const network_handler::ErrorCallback& error_callback) { 286 ShillManagerClient* manager = 287 DBusThreadManager::Get()->GetShillManagerClient(); 288 std::string type; 289 properties.GetStringWithoutPathExpansion(flimflam::kTypeProperty, &type); 290 291 NET_LOG_USER("CreateConfiguration", type); 292 LogConfigProperties("Configure", type, properties); 293 294 std::string profile; 295 properties.GetStringWithoutPathExpansion(flimflam::kProfileProperty, 296 &profile); 297 DCHECK(!profile.empty()); 298 manager->ConfigureServiceForProfile( 299 dbus::ObjectPath(profile), 300 properties, 301 base::Bind(&NetworkConfigurationHandler::RunCreateNetworkCallback, 302 AsWeakPtr(), 303 callback), 304 base::Bind(&network_handler::ShillErrorCallbackFunction, 305 "Config.CreateConfiguration Failed", 306 "", 307 error_callback)); 308} 309 310void NetworkConfigurationHandler::RemoveConfiguration( 311 const std::string& service_path, 312 const base::Closure& callback, 313 const network_handler::ErrorCallback& error_callback) { 314 // Service.Remove is not reliable. Instead, request the profile entries 315 // for the service and remove each entry. 316 if (profile_entry_deleters_.count(service_path)) { 317 InvokeErrorCallback( 318 "RemoveConfiguration In-Progress", service_path, error_callback); 319 return; 320 } 321 NET_LOG_USER("Remove Configuration", service_path); 322 ProfileEntryDeleter* deleter = 323 new ProfileEntryDeleter(this, service_path, callback, error_callback); 324 profile_entry_deleters_[service_path] = deleter; 325 deleter->Run(); 326} 327 328void NetworkConfigurationHandler::SetNetworkProfile( 329 const std::string& service_path, 330 const std::string& profile_path, 331 const base::Closure& callback, 332 const network_handler::ErrorCallback& error_callback) { 333 NET_LOG_USER("SetNetworkProfile", service_path + ": " + profile_path); 334 base::StringValue profile_path_value(profile_path); 335 DBusThreadManager::Get()->GetShillServiceClient()->SetProperty( 336 dbus::ObjectPath(service_path), 337 flimflam::kProfileProperty, 338 profile_path_value, 339 callback, 340 base::Bind(&SetNetworkProfileErrorCallback, 341 service_path, profile_path, error_callback)); 342} 343 344// NetworkConfigurationHandler Private methods 345 346NetworkConfigurationHandler::NetworkConfigurationHandler() 347 : network_state_handler_(NULL) { 348} 349 350NetworkConfigurationHandler::~NetworkConfigurationHandler() { 351 STLDeleteContainerPairSecondPointers( 352 profile_entry_deleters_.begin(), profile_entry_deleters_.end()); 353} 354 355void NetworkConfigurationHandler::Init( 356 NetworkStateHandler* network_state_handler) { 357 network_state_handler_ = network_state_handler; 358} 359 360void NetworkConfigurationHandler::RunCreateNetworkCallback( 361 const network_handler::StringResultCallback& callback, 362 const dbus::ObjectPath& service_path) { 363 if (!callback.is_null()) 364 callback.Run(service_path.value()); 365 // This may also get called when CreateConfiguration is used to update an 366 // existing configuration, so request a service update just in case. 367 // TODO(pneubeck): Separate 'Create' and 'Update' calls and only trigger 368 // this on an update. 369 network_state_handler_->RequestUpdateForNetwork(service_path.value()); 370} 371 372void NetworkConfigurationHandler::ProfileEntryDeleterCompleted( 373 const std::string& service_path) { 374 std::map<std::string, ProfileEntryDeleter*>::iterator iter = 375 profile_entry_deleters_.find(service_path); 376 DCHECK(iter != profile_entry_deleters_.end()); 377 delete iter->second; 378 profile_entry_deleters_.erase(iter); 379} 380 381void NetworkConfigurationHandler::SetPropertiesSuccessCallback( 382 const std::string& service_path, 383 const base::Closure& callback) { 384 if (!callback.is_null()) 385 callback.Run(); 386 network_state_handler_->RequestUpdateForNetwork(service_path); 387} 388 389void NetworkConfigurationHandler::SetPropertiesErrorCallback( 390 const std::string& service_path, 391 const network_handler::ErrorCallback& error_callback, 392 const std::string& dbus_error_name, 393 const std::string& dbus_error_message) { 394 network_handler::ShillErrorCallbackFunction( 395 "Config.SetProperties Failed", 396 service_path, error_callback, 397 dbus_error_name, dbus_error_message); 398 // Some properties may have changed so request an update regardless. 399 network_state_handler_->RequestUpdateForNetwork(service_path); 400} 401 402void NetworkConfigurationHandler::ClearPropertiesSuccessCallback( 403 const std::string& service_path, 404 const std::vector<std::string>& names, 405 const base::Closure& callback, 406 const network_handler::ErrorCallback& error_callback, 407 const base::ListValue& result) { 408 const std::string kClearPropertiesFailedError("Error.ClearPropertiesFailed"); 409 DCHECK(names.size() == result.GetSize()) 410 << "Incorrect result size from ClearProperties."; 411 412 bool some_failed = false; 413 for (size_t i = 0; i < result.GetSize(); ++i) { 414 bool success = false; 415 result.GetBoolean(i, &success); 416 if (!success) { 417 NET_LOG_ERROR("ClearProperties Failed: " + names[i], service_path); 418 some_failed = true; 419 } 420 } 421 422 if (some_failed) { 423 if (!error_callback.is_null()) { 424 scoped_ptr<base::DictionaryValue> error_data( 425 network_handler::CreateErrorData( 426 service_path, kClearPropertiesFailedError, 427 base::StringPrintf("Errors: %" PRIuS, result.GetSize()))); 428 error_data->Set("errors", result.DeepCopy()); 429 scoped_ptr<base::ListValue> name_list(new base::ListValue); 430 name_list->AppendStrings(names); 431 error_data->Set("names", name_list.release()); 432 error_callback.Run(kClearPropertiesFailedError, error_data.Pass()); 433 } 434 } else if (!callback.is_null()) { 435 callback.Run(); 436 } 437 network_state_handler_->RequestUpdateForNetwork(service_path); 438} 439 440void NetworkConfigurationHandler::ClearPropertiesErrorCallback( 441 const std::string& service_path, 442 const network_handler::ErrorCallback& error_callback, 443 const std::string& dbus_error_name, 444 const std::string& dbus_error_message) { 445 network_handler::ShillErrorCallbackFunction( 446 "Config.ClearProperties Failed", 447 service_path, error_callback, 448 dbus_error_name, dbus_error_message); 449 // Some properties may have changed so request an update regardless. 450 network_state_handler_->RequestUpdateForNetwork(service_path); 451} 452 453// static 454NetworkConfigurationHandler* NetworkConfigurationHandler::InitializeForTest( 455 NetworkStateHandler* network_state_handler) { 456 NetworkConfigurationHandler* handler = new NetworkConfigurationHandler(); 457 handler->Init(network_state_handler); 458 return handler; 459} 460 461} // namespace chromeos 462