Some additional network settings cleanup
[chromium-blink-merge.git] / chromeos / network / shill_property_util.cc
blob405de1e3877dbd6b62288a55b0ce5b9536a6c6fa
1 // Copyright 2013 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 "chromeos/network/shill_property_util.h"
7 #include "base/i18n/icu_encoding_detection.h"
8 #include "base/i18n/icu_string_conversions.h"
9 #include "base/json/json_writer.h"
10 #include "base/strings/string_number_conversions.h"
11 #include "base/strings/string_util.h"
12 #include "base/strings/stringprintf.h"
13 #include "base/strings/utf_string_conversion_utils.h"
14 #include "base/values.h"
15 #include "chromeos/network/network_event_log.h"
16 #include "chromeos/network/network_ui_data.h"
17 #include "chromeos/network/onc/onc_utils.h"
18 #include "third_party/cros_system_api/dbus/service_constants.h"
20 namespace chromeos {
22 namespace shill_property_util {
24 namespace {
26 // Replace non UTF8 characters in |str| with a replacement character.
27 std::string ValidateUTF8(const std::string& str) {
28 std::string result;
29 for (int32 index = 0; index < static_cast<int32>(str.size()); ++index) {
30 uint32 code_point_out;
31 bool is_unicode_char = base::ReadUnicodeCharacter(
32 str.c_str(), str.size(), &index, &code_point_out);
33 const uint32 kFirstNonControlChar = 0x20;
34 if (is_unicode_char && (code_point_out >= kFirstNonControlChar)) {
35 base::WriteUnicodeCharacter(code_point_out, &result);
36 } else {
37 const uint32 kReplacementChar = 0xFFFD;
38 // Puts kReplacementChar if character is a control character [0,0x20)
39 // or is not readable UTF8.
40 base::WriteUnicodeCharacter(kReplacementChar, &result);
43 return result;
46 // If existent and non-empty, copies the string at |key| from |source| to
47 // |dest|. Returns true if the string was copied.
48 bool CopyStringFromDictionary(const base::DictionaryValue& source,
49 const std::string& key,
50 base::DictionaryValue* dest) {
51 std::string string_value;
52 if (!source.GetStringWithoutPathExpansion(key, &string_value) ||
53 string_value.empty()) {
54 return false;
56 dest->SetStringWithoutPathExpansion(key, string_value);
57 return true;
60 // This is the same normalization that Shill applies to security types for the
61 // sake of comparing/identifying WiFi networks. See Shill's
62 // WiFiService::GetSecurityClass.
63 std::string GetSecurityClass(const std::string& security) {
64 if (security == shill::kSecurityRsn || security == shill::kSecurityWpa)
65 return shill::kSecurityPsk;
66 else
67 return security;
70 } // namespace
72 void SetSSID(const std::string ssid, base::DictionaryValue* properties) {
73 std::string hex_ssid = base::HexEncode(ssid.c_str(), ssid.size());
74 properties->SetStringWithoutPathExpansion(shill::kWifiHexSsid, hex_ssid);
77 std::string GetSSIDFromProperties(const base::DictionaryValue& properties,
78 bool* unknown_encoding) {
79 bool verbose_logging = false;
80 if (unknown_encoding) {
81 *unknown_encoding = false;
82 verbose_logging = true;
85 // Get name for debugging.
86 std::string name;
87 properties.GetStringWithoutPathExpansion(shill::kNameProperty, &name);
89 std::string hex_ssid;
90 properties.GetStringWithoutPathExpansion(shill::kWifiHexSsid, &hex_ssid);
92 if (hex_ssid.empty()) {
93 if (verbose_logging)
94 NET_LOG_DEBUG("GetSSIDFromProperties: No HexSSID set.", name);
95 return std::string();
98 std::string ssid;
99 std::vector<uint8> raw_ssid_bytes;
100 if (base::HexStringToBytes(hex_ssid, &raw_ssid_bytes)) {
101 ssid = std::string(raw_ssid_bytes.begin(), raw_ssid_bytes.end());
102 if (verbose_logging) {
103 NET_LOG_DEBUG(base::StringPrintf("GetSSIDFromProperties: %s, SSID: %s",
104 hex_ssid.c_str(), ssid.c_str()), name);
106 } else {
107 NET_LOG_ERROR(
108 base::StringPrintf("GetSSIDFromProperties: Error processing: %s",
109 hex_ssid.c_str()), name);
110 return std::string();
113 if (base::IsStringUTF8(ssid))
114 return ssid;
116 // Detect encoding and convert to UTF-8.
117 std::string encoding;
118 if (!base::DetectEncoding(ssid, &encoding)) {
119 // TODO(stevenjb): This is currently experimental. If we find a case where
120 // base::DetectEncoding() fails, we need to figure out whether we can use
121 // country_code with ConvertToUtf8(). crbug.com/233267.
122 properties.GetStringWithoutPathExpansion(shill::kCountryProperty,
123 &encoding);
125 std::string utf8_ssid;
126 if (!encoding.empty() &&
127 base::ConvertToUtf8AndNormalize(ssid, encoding, &utf8_ssid)) {
128 if (utf8_ssid != ssid) {
129 if (verbose_logging) {
130 NET_LOG_DEBUG(
131 base::StringPrintf("GetSSIDFromProperties: Encoding=%s: %s",
132 encoding.c_str(), utf8_ssid.c_str()), name);
135 return utf8_ssid;
138 if (unknown_encoding)
139 *unknown_encoding = true;
140 if (verbose_logging) {
141 NET_LOG_DEBUG(
142 base::StringPrintf("GetSSIDFromProperties: Unrecognized Encoding=%s",
143 encoding.c_str()), name);
145 return ssid;
148 std::string GetNetworkIdFromProperties(
149 const base::DictionaryValue& properties) {
150 if (properties.empty())
151 return "EmptyProperties";
152 std::string result;
153 if (properties.GetStringWithoutPathExpansion(shill::kGuidProperty, &result))
154 return result;
155 if (properties.GetStringWithoutPathExpansion(shill::kSSIDProperty, &result))
156 return result;
157 if (properties.GetStringWithoutPathExpansion(shill::kNameProperty, &result))
158 return result;
159 std::string type = "UnknownType";
160 properties.GetStringWithoutPathExpansion(shill::kTypeProperty, &type);
161 return "Unidentified " + type;
164 std::string GetNameFromProperties(const std::string& service_path,
165 const base::DictionaryValue& properties) {
166 std::string name;
167 properties.GetStringWithoutPathExpansion(shill::kNameProperty, &name);
169 std::string validated_name = ValidateUTF8(name);
170 if (validated_name != name) {
171 NET_LOG_DEBUG("GetNameFromProperties",
172 base::StringPrintf("Validated name %s: UTF8: %s",
173 service_path.c_str(),
174 validated_name.c_str()));
177 std::string type;
178 properties.GetStringWithoutPathExpansion(shill::kTypeProperty, &type);
179 if (type.empty()) {
180 NET_LOG_ERROR("GetNameFromProperties: No type", service_path);
181 return validated_name;
183 if (!NetworkTypePattern::WiFi().MatchesType(type))
184 return validated_name;
186 bool unknown_ssid_encoding = false;
187 std::string ssid = GetSSIDFromProperties(properties, &unknown_ssid_encoding);
188 if (ssid.empty())
189 NET_LOG_ERROR("GetNameFromProperties", "No SSID set: " + service_path);
191 // Use |validated_name| if |ssid| is empty.
192 // And if the encoding of the SSID is unknown, use |ssid|, which contains raw
193 // bytes in that case, only if |validated_name| is empty.
194 if (ssid.empty() || (unknown_ssid_encoding && !validated_name.empty()))
195 return validated_name;
197 if (ssid != validated_name) {
198 NET_LOG_DEBUG("GetNameFromProperties",
199 base::StringPrintf("%s: SSID: %s, Name: %s",
200 service_path.c_str(),
201 ssid.c_str(),
202 validated_name.c_str()));
204 return ssid;
207 scoped_ptr<NetworkUIData> GetUIDataFromValue(const base::Value& ui_data_value) {
208 std::string ui_data_str;
209 if (!ui_data_value.GetAsString(&ui_data_str))
210 return scoped_ptr<NetworkUIData>();
211 if (ui_data_str.empty())
212 return make_scoped_ptr(new NetworkUIData());
213 scoped_ptr<base::DictionaryValue> ui_data_dict(
214 chromeos::onc::ReadDictionaryFromJson(ui_data_str));
215 if (!ui_data_dict)
216 return scoped_ptr<NetworkUIData>();
217 return make_scoped_ptr(new NetworkUIData(*ui_data_dict));
220 scoped_ptr<NetworkUIData> GetUIDataFromProperties(
221 const base::DictionaryValue& shill_dictionary) {
222 const base::Value* ui_data_value = NULL;
223 shill_dictionary.GetWithoutPathExpansion(shill::kUIDataProperty,
224 &ui_data_value);
225 if (!ui_data_value) {
226 VLOG(2) << "Dictionary has no UIData entry.";
227 return scoped_ptr<NetworkUIData>();
229 scoped_ptr<NetworkUIData> ui_data = GetUIDataFromValue(*ui_data_value);
230 if (!ui_data)
231 LOG(ERROR) << "UIData is not a valid JSON dictionary.";
232 return ui_data.Pass();
235 void SetUIData(const NetworkUIData& ui_data,
236 base::DictionaryValue* shill_dictionary) {
237 base::DictionaryValue ui_data_dict;
238 ui_data.FillDictionary(&ui_data_dict);
239 std::string ui_data_blob;
240 base::JSONWriter::Write(&ui_data_dict, &ui_data_blob);
241 shill_dictionary->SetStringWithoutPathExpansion(shill::kUIDataProperty,
242 ui_data_blob);
245 bool CopyIdentifyingProperties(const base::DictionaryValue& service_properties,
246 const bool properties_read_from_shill,
247 base::DictionaryValue* dest) {
248 bool success = true;
250 // GUID is optional.
251 CopyStringFromDictionary(service_properties, shill::kGuidProperty, dest);
253 std::string type;
254 service_properties.GetStringWithoutPathExpansion(shill::kTypeProperty, &type);
255 success &= !type.empty();
256 dest->SetStringWithoutPathExpansion(shill::kTypeProperty, type);
257 if (type == shill::kTypeWifi) {
258 std::string security;
259 service_properties.GetStringWithoutPathExpansion(shill::kSecurityProperty,
260 &security);
261 if (security.empty()) {
262 success = false;
263 } else {
264 dest->SetStringWithoutPathExpansion(shill::kSecurityProperty,
265 GetSecurityClass(security));
267 success &=
268 CopyStringFromDictionary(service_properties, shill::kWifiHexSsid, dest);
269 success &= CopyStringFromDictionary(
270 service_properties, shill::kModeProperty, dest);
271 } else if (type == shill::kTypeVPN) {
272 success &= CopyStringFromDictionary(
273 service_properties, shill::kNameProperty, dest);
275 // VPN Provider values are read from the "Provider" dictionary, but written
276 // with the keys "Provider.Type" and "Provider.Host".
277 // TODO(pneubeck): Simplify this once http://crbug.com/381135 is fixed.
278 std::string vpn_provider_type;
279 std::string vpn_provider_host;
280 if (properties_read_from_shill) {
281 const base::DictionaryValue* provider_properties = NULL;
282 if (!service_properties.GetDictionaryWithoutPathExpansion(
283 shill::kProviderProperty, &provider_properties)) {
284 NET_LOG_ERROR("Missing VPN provider dict",
285 GetNetworkIdFromProperties(service_properties));
287 provider_properties->GetStringWithoutPathExpansion(shill::kTypeProperty,
288 &vpn_provider_type);
289 provider_properties->GetStringWithoutPathExpansion(shill::kHostProperty,
290 &vpn_provider_host);
291 } else {
292 service_properties.GetStringWithoutPathExpansion(
293 shill::kProviderTypeProperty, &vpn_provider_type);
294 service_properties.GetStringWithoutPathExpansion(
295 shill::kProviderHostProperty, &vpn_provider_host);
297 success &= !vpn_provider_type.empty();
298 dest->SetStringWithoutPathExpansion(shill::kProviderTypeProperty,
299 vpn_provider_type);
301 success &= !vpn_provider_host.empty();
302 dest->SetStringWithoutPathExpansion(shill::kProviderHostProperty,
303 vpn_provider_host);
304 } else if (type == shill::kTypeEthernet || type == shill::kTypeEthernetEap) {
305 // Ethernet and EthernetEAP don't have any additional identifying
306 // properties.
307 } else {
308 NOTREACHED() << "Unsupported network type " << type;
309 success = false;
311 if (!success) {
312 NET_LOG_ERROR("Missing required properties",
313 GetNetworkIdFromProperties(service_properties));
315 return success;
318 bool DoIdentifyingPropertiesMatch(const base::DictionaryValue& new_properties,
319 const base::DictionaryValue& old_properties) {
320 base::DictionaryValue new_identifying;
321 if (!CopyIdentifyingProperties(
322 new_properties,
323 false /* properties were not read from Shill */,
324 &new_identifying)) {
325 return false;
327 base::DictionaryValue old_identifying;
328 if (!CopyIdentifyingProperties(old_properties,
329 true /* properties were read from Shill */,
330 &old_identifying)) {
331 return false;
334 return new_identifying.Equals(&old_identifying);
337 bool IsPassphraseKey(const std::string& key) {
338 return key == shill::kEapPrivateKeyPasswordProperty ||
339 key == shill::kEapPasswordProperty ||
340 key == shill::kL2tpIpsecPasswordProperty ||
341 key == shill::kOpenVPNPasswordProperty ||
342 key == shill::kOpenVPNAuthUserPassProperty ||
343 key == shill::kOpenVPNTLSAuthContentsProperty ||
344 key == shill::kPassphraseProperty ||
345 key == shill::kOpenVPNOTPProperty ||
346 key == shill::kEapPrivateKeyProperty ||
347 key == shill::kEapPinProperty ||
348 key == shill::kApnPasswordProperty;
351 bool GetHomeProviderFromProperty(const base::Value& value,
352 std::string* home_provider_id) {
353 const base::DictionaryValue* dict = NULL;
354 if (!value.GetAsDictionary(&dict))
355 return false;
356 std::string home_provider_country;
357 std::string home_provider_name;
358 dict->GetStringWithoutPathExpansion(shill::kOperatorCountryKey,
359 &home_provider_country);
360 dict->GetStringWithoutPathExpansion(shill::kOperatorNameKey,
361 &home_provider_name);
362 // Set home_provider_id
363 if (!home_provider_name.empty() && !home_provider_country.empty()) {
364 *home_provider_id = base::StringPrintf(
365 "%s (%s)", home_provider_name.c_str(), home_provider_country.c_str());
366 } else {
367 if (!dict->GetStringWithoutPathExpansion(shill::kOperatorCodeKey,
368 home_provider_id)) {
369 return false;
371 LOG(WARNING)
372 << "Provider name and country not defined, using code instead: "
373 << *home_provider_id;
375 return true;
378 } // namespace shill_property_util
380 } // namespace chromeos