Home | History | Annotate | Download | only in network
      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.
      4 
      5 #include "chromeos/network/shill_property_util.h"
      6 
      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"
     19 
     20 namespace chromeos {
     21 
     22 namespace shill_property_util {
     23 
     24 namespace {
     25 
     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);
     41     }
     42   }
     43   return result;
     44 }
     45 
     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;
     55   dest->SetStringWithoutPathExpansion(key, string_value);
     56   return true;
     57 }
     58 
     59 }  // namespace
     60 
     61 void SetSSID(const std::string ssid, base::DictionaryValue* properties) {
     62   std::string hex_ssid = base::HexEncode(ssid.c_str(), ssid.size());
     63   properties->SetStringWithoutPathExpansion(shill::kWifiHexSsid, hex_ssid);
     64 }
     65 
     66 std::string GetSSIDFromProperties(const base::DictionaryValue& properties,
     67                                   bool* unknown_encoding) {
     68   if (unknown_encoding)
     69     *unknown_encoding = false;
     70   std::string hex_ssid;
     71   properties.GetStringWithoutPathExpansion(shill::kWifiHexSsid, &hex_ssid);
     72 
     73   if (hex_ssid.empty()) {
     74     NET_LOG_ERROR("GetSSIDFromProperties", "No HexSSID set.");
     75     return std::string();
     76   }
     77 
     78   std::string ssid;
     79   std::vector<uint8> raw_ssid_bytes;
     80   if (base::HexStringToBytes(hex_ssid, &raw_ssid_bytes)) {
     81     ssid = std::string(raw_ssid_bytes.begin(), raw_ssid_bytes.end());
     82     NET_LOG_DEBUG(
     83         "GetSSIDFromProperties",
     84         base::StringPrintf("%s, SSID: %s", hex_ssid.c_str(), ssid.c_str()));
     85   } else {
     86     NET_LOG_ERROR("GetSSIDFromProperties",
     87                   base::StringPrintf("Error processing: %s", hex_ssid.c_str()));
     88     return std::string();
     89   }
     90 
     91   if (IsStringUTF8(ssid))
     92     return ssid;
     93 
     94   // Detect encoding and convert to UTF-8.
     95   std::string encoding;
     96   if (!base::DetectEncoding(ssid, &encoding)) {
     97     // TODO(stevenjb): This is currently experimental. If we find a case where
     98     // base::DetectEncoding() fails, we need to figure out whether we can use
     99     // country_code with ConvertToUtf8(). crbug.com/233267.
    100     properties.GetStringWithoutPathExpansion(shill::kCountryProperty,
    101                                              &encoding);
    102   }
    103   std::string utf8_ssid;
    104   if (!encoding.empty() &&
    105       base::ConvertToUtf8AndNormalize(ssid, encoding, &utf8_ssid)) {
    106     if (utf8_ssid != ssid) {
    107       NET_LOG_DEBUG(
    108           "GetSSIDFromProperties",
    109           base::StringPrintf(
    110               "Encoding=%s: %s", encoding.c_str(), utf8_ssid.c_str()));
    111     }
    112     return utf8_ssid;
    113   }
    114 
    115   if (unknown_encoding)
    116     *unknown_encoding = true;
    117   NET_LOG_DEBUG(
    118       "GetSSIDFromProperties",
    119       base::StringPrintf("Unrecognized Encoding=%s", encoding.c_str()));
    120   return ssid;
    121 }
    122 
    123 std::string GetNameFromProperties(const std::string& service_path,
    124                                   const base::DictionaryValue& properties) {
    125   std::string name;
    126   properties.GetStringWithoutPathExpansion(shill::kNameProperty, &name);
    127 
    128   std::string validated_name = ValidateUTF8(name);
    129   if (validated_name != name) {
    130     NET_LOG_DEBUG("GetNameFromProperties",
    131                   base::StringPrintf("Validated name %s: UTF8: %s",
    132                                      service_path.c_str(),
    133                                      validated_name.c_str()));
    134   }
    135 
    136   std::string type;
    137   properties.GetStringWithoutPathExpansion(shill::kTypeProperty, &type);
    138   if (!NetworkTypePattern::WiFi().MatchesType(type))
    139     return validated_name;
    140 
    141   bool unknown_ssid_encoding = false;
    142   std::string ssid = GetSSIDFromProperties(properties, &unknown_ssid_encoding);
    143   if (ssid.empty())
    144     NET_LOG_ERROR("GetNameFromProperties", "No SSID set: " + service_path);
    145 
    146   // Use |validated_name| if |ssid| is empty.
    147   // And if the encoding of the SSID is unknown, use |ssid|, which contains raw
    148   // bytes in that case, only if |validated_name| is empty.
    149   if (ssid.empty() || (unknown_ssid_encoding && !validated_name.empty()))
    150     return validated_name;
    151 
    152   if (ssid != validated_name) {
    153     NET_LOG_DEBUG("GetNameFromProperties",
    154                   base::StringPrintf("%s: SSID: %s, Name: %s",
    155                                      service_path.c_str(),
    156                                      ssid.c_str(),
    157                                      validated_name.c_str()));
    158   }
    159   return ssid;
    160 }
    161 
    162 scoped_ptr<NetworkUIData> GetUIDataFromValue(const base::Value& ui_data_value) {
    163   std::string ui_data_str;
    164   if (!ui_data_value.GetAsString(&ui_data_str))
    165     return scoped_ptr<NetworkUIData>();
    166   if (ui_data_str.empty())
    167     return make_scoped_ptr(new NetworkUIData());
    168   scoped_ptr<base::DictionaryValue> ui_data_dict(
    169       chromeos::onc::ReadDictionaryFromJson(ui_data_str));
    170   if (!ui_data_dict)
    171     return scoped_ptr<NetworkUIData>();
    172   return make_scoped_ptr(new NetworkUIData(*ui_data_dict));
    173 }
    174 
    175 scoped_ptr<NetworkUIData> GetUIDataFromProperties(
    176     const base::DictionaryValue& shill_dictionary) {
    177   const base::Value* ui_data_value = NULL;
    178   shill_dictionary.GetWithoutPathExpansion(shill::kUIDataProperty,
    179                                            &ui_data_value);
    180   if (!ui_data_value) {
    181     VLOG(2) << "Dictionary has no UIData entry.";
    182     return scoped_ptr<NetworkUIData>();
    183   }
    184   scoped_ptr<NetworkUIData> ui_data = GetUIDataFromValue(*ui_data_value);
    185   if (!ui_data)
    186     LOG(ERROR) << "UIData is not a valid JSON dictionary.";
    187   return ui_data.Pass();
    188 }
    189 
    190 void SetUIData(const NetworkUIData& ui_data,
    191                base::DictionaryValue* shill_dictionary) {
    192   base::DictionaryValue ui_data_dict;
    193   ui_data.FillDictionary(&ui_data_dict);
    194   std::string ui_data_blob;
    195   base::JSONWriter::Write(&ui_data_dict, &ui_data_blob);
    196   shill_dictionary->SetStringWithoutPathExpansion(shill::kUIDataProperty,
    197                                                   ui_data_blob);
    198 }
    199 
    200 bool CopyIdentifyingProperties(const base::DictionaryValue& service_properties,
    201                                base::DictionaryValue* dest) {
    202   bool success = true;
    203 
    204   // GUID is optional.
    205   CopyStringFromDictionary(service_properties, shill::kGuidProperty, dest);
    206 
    207   std::string type;
    208   service_properties.GetStringWithoutPathExpansion(shill::kTypeProperty, &type);
    209   success &= !type.empty();
    210   dest->SetStringWithoutPathExpansion(shill::kTypeProperty, type);
    211   if (type == shill::kTypeWifi) {
    212     success &= CopyStringFromDictionary(
    213         service_properties, shill::kSecurityProperty, dest);
    214     success &=
    215         CopyStringFromDictionary(service_properties, shill::kWifiHexSsid, dest);
    216     success &= CopyStringFromDictionary(
    217         service_properties, shill::kModeProperty, dest);
    218   } else if (type == shill::kTypeVPN) {
    219     success &= CopyStringFromDictionary(
    220         service_properties, shill::kNameProperty, dest);
    221     // VPN Provider values are read from the "Provider" dictionary, but written
    222     // with the keys "Provider.Type" and "Provider.Host".
    223     const base::DictionaryValue* provider_properties = NULL;
    224     if (!service_properties.GetDictionaryWithoutPathExpansion(
    225              shill::kProviderProperty, &provider_properties)) {
    226       NET_LOG_ERROR("CopyIdentifyingProperties", "Missing VPN provider dict");
    227       return false;
    228     }
    229     std::string vpn_provider_type;
    230     provider_properties->GetStringWithoutPathExpansion(shill::kTypeProperty,
    231                                                        &vpn_provider_type);
    232     success &= !vpn_provider_type.empty();
    233     dest->SetStringWithoutPathExpansion(shill::kProviderTypeProperty,
    234                                         vpn_provider_type);
    235 
    236     std::string vpn_provider_host;
    237     provider_properties->GetStringWithoutPathExpansion(shill::kHostProperty,
    238                                                        &vpn_provider_host);
    239     success &= !vpn_provider_host.empty();
    240     dest->SetStringWithoutPathExpansion(shill::kProviderHostProperty,
    241                                         vpn_provider_host);
    242   } else if (type == shill::kTypeEthernet || type == shill::kTypeEthernetEap) {
    243     // Ethernet and EthernetEAP don't have any additional identifying
    244     // properties.
    245   } else {
    246     NOTREACHED() << "Unsupported network type " << type;
    247     success = false;
    248   }
    249   if (!success)
    250     NET_LOG_ERROR("CopyIdentifyingProperties", "Missing required properties");
    251   return success;
    252 }
    253 
    254 bool DoIdentifyingPropertiesMatch(const base::DictionaryValue& properties_a,
    255                                   const base::DictionaryValue& properties_b) {
    256   base::DictionaryValue identifying_a;
    257   if (!CopyIdentifyingProperties(properties_a, &identifying_a))
    258     return false;
    259   base::DictionaryValue identifying_b;
    260   if (!CopyIdentifyingProperties(properties_b, &identifying_b))
    261     return false;
    262 
    263   return identifying_a.Equals(&identifying_b);
    264 }
    265 
    266 }  // namespace shill_property_util
    267 
    268 namespace {
    269 
    270 const char kPatternDefault[] = "PatternDefault";
    271 const char kPatternEthernet[] = "PatternEthernet";
    272 const char kPatternWireless[] = "PatternWireless";
    273 const char kPatternMobile[] = "PatternMobile";
    274 const char kPatternNonVirtual[] = "PatternNonVirtual";
    275 
    276 enum NetworkTypeBitFlag {
    277   kNetworkTypeNone = 0,
    278   kNetworkTypeEthernet = 1 << 0,
    279   kNetworkTypeWifi = 1 << 1,
    280   kNetworkTypeWimax = 1 << 2,
    281   kNetworkTypeCellular = 1 << 3,
    282   kNetworkTypeVPN = 1 << 4,
    283   kNetworkTypeEthernetEap = 1 << 5,
    284   kNetworkTypeBluetooth = 1 << 6
    285 };
    286 
    287 struct ShillToBitFlagEntry {
    288   const char* shill_network_type;
    289   NetworkTypeBitFlag bit_flag;
    290 } shill_type_to_flag[] = {
    291   { shill::kTypeEthernet, kNetworkTypeEthernet },
    292   { shill::kTypeEthernetEap, kNetworkTypeEthernetEap },
    293   { shill::kTypeWifi, kNetworkTypeWifi },
    294   { shill::kTypeWimax, kNetworkTypeWimax },
    295   { shill::kTypeCellular, kNetworkTypeCellular },
    296   { shill::kTypeVPN, kNetworkTypeVPN },
    297   { shill::kTypeBluetooth, kNetworkTypeBluetooth }
    298 };
    299 
    300 NetworkTypeBitFlag ShillNetworkTypeToFlag(const std::string& shill_type) {
    301   for (size_t i = 0; i < arraysize(shill_type_to_flag); ++i) {
    302     if (shill_type_to_flag[i].shill_network_type == shill_type)
    303       return shill_type_to_flag[i].bit_flag;
    304   }
    305   NET_LOG_ERROR("ShillNetworkTypeToFlag", "Unknown type: " + shill_type);
    306   return kNetworkTypeNone;
    307 }
    308 
    309 }  // namespace
    310 
    311 // static
    312 NetworkTypePattern NetworkTypePattern::Default() {
    313   return NetworkTypePattern(~0);
    314 }
    315 
    316 // static
    317 NetworkTypePattern NetworkTypePattern::Wireless() {
    318   return NetworkTypePattern(kNetworkTypeWifi | kNetworkTypeWimax |
    319                             kNetworkTypeCellular);
    320 }
    321 
    322 // static
    323 NetworkTypePattern NetworkTypePattern::Mobile() {
    324   return NetworkTypePattern(kNetworkTypeCellular | kNetworkTypeWimax);
    325 }
    326 
    327 // static
    328 NetworkTypePattern NetworkTypePattern::NonVirtual() {
    329   return NetworkTypePattern(~kNetworkTypeVPN);
    330 }
    331 
    332 // static
    333 NetworkTypePattern NetworkTypePattern::Ethernet() {
    334   return NetworkTypePattern(kNetworkTypeEthernet);
    335 }
    336 
    337 // static
    338 NetworkTypePattern NetworkTypePattern::WiFi() {
    339   return NetworkTypePattern(kNetworkTypeWifi);
    340 }
    341 
    342 // static
    343 NetworkTypePattern NetworkTypePattern::Cellular() {
    344   return NetworkTypePattern(kNetworkTypeCellular);
    345 }
    346 
    347 // static
    348 NetworkTypePattern NetworkTypePattern::VPN() {
    349   return NetworkTypePattern(kNetworkTypeVPN);
    350 }
    351 
    352 // static
    353 NetworkTypePattern NetworkTypePattern::Wimax() {
    354   return NetworkTypePattern(kNetworkTypeWimax);
    355 }
    356 
    357 // static
    358 NetworkTypePattern NetworkTypePattern::Primitive(
    359     const std::string& shill_network_type) {
    360   return NetworkTypePattern(ShillNetworkTypeToFlag(shill_network_type));
    361 }
    362 
    363 bool NetworkTypePattern::Equals(const NetworkTypePattern& other) const {
    364   return pattern_ == other.pattern_;
    365 }
    366 
    367 bool NetworkTypePattern::MatchesType(
    368     const std::string& shill_network_type) const {
    369   return MatchesPattern(Primitive(shill_network_type));
    370 }
    371 
    372 bool NetworkTypePattern::MatchesPattern(
    373     const NetworkTypePattern& other_pattern) const {
    374   if (Equals(other_pattern))
    375     return true;
    376 
    377   return pattern_ & other_pattern.pattern_;
    378 }
    379 
    380 std::string NetworkTypePattern::ToDebugString() const {
    381   if (Equals(Default()))
    382     return kPatternDefault;
    383   if (Equals(Ethernet()))
    384     return kPatternEthernet;
    385   if (Equals(Wireless()))
    386     return kPatternWireless;
    387   if (Equals(Mobile()))
    388     return kPatternMobile;
    389   if (Equals(NonVirtual()))
    390     return kPatternNonVirtual;
    391 
    392   std::string str;
    393   for (size_t i = 0; i < arraysize(shill_type_to_flag); ++i) {
    394     if (!(pattern_ & shill_type_to_flag[i].bit_flag))
    395       continue;
    396     if (!str.empty())
    397       str += "|";
    398     str += shill_type_to_flag[i].shill_network_type;
    399   }
    400   return str;
    401 }
    402 
    403 NetworkTypePattern::NetworkTypePattern(int pattern) : pattern_(pattern) {}
    404 
    405 }  // namespace chromeos
    406