Home | History | Annotate | Download | only in onc
      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/onc/onc_translator.h"
      6 
      7 #include <string>
      8 
      9 #include "base/basictypes.h"
     10 #include "base/json/json_reader.h"
     11 #include "base/json/json_writer.h"
     12 #include "base/logging.h"
     13 #include "base/values.h"
     14 #include "chromeos/network/network_state.h"
     15 #include "chromeos/network/onc/onc_signature.h"
     16 #include "chromeos/network/onc/onc_translation_tables.h"
     17 #include "chromeos/network/shill_property_util.h"
     18 #include "components/onc/onc_constants.h"
     19 #include "third_party/cros_system_api/dbus/service_constants.h"
     20 
     21 namespace chromeos {
     22 namespace onc {
     23 
     24 namespace {
     25 
     26 // Converts |str| to a base::Value of the given |type|. If the conversion fails,
     27 // returns NULL.
     28 scoped_ptr<base::Value> ConvertStringToValue(const std::string& str,
     29                                              base::Value::Type type) {
     30   base::Value* value;
     31   if (type == base::Value::TYPE_STRING) {
     32     value = base::Value::CreateStringValue(str);
     33   } else {
     34     value = base::JSONReader::Read(str);
     35   }
     36 
     37   if (value == NULL || value->GetType() != type) {
     38     delete value;
     39     value = NULL;
     40   }
     41   return make_scoped_ptr(value);
     42 }
     43 
     44 // This class implements the translation of properties from the given
     45 // |shill_dictionary| to a new ONC object of signature |onc_signature|. Using
     46 // recursive calls to CreateTranslatedONCObject of new instances, nested objects
     47 // are translated.
     48 class ShillToONCTranslator {
     49  public:
     50   ShillToONCTranslator(const base::DictionaryValue& shill_dictionary,
     51                        const OncValueSignature& onc_signature)
     52       : shill_dictionary_(&shill_dictionary),
     53         onc_signature_(&onc_signature) {
     54     field_translation_table_ = GetFieldTranslationTable(onc_signature);
     55   }
     56 
     57   // Translates the associated Shill dictionary and creates an ONC object of the
     58   // given signature.
     59   scoped_ptr<base::DictionaryValue> CreateTranslatedONCObject();
     60 
     61  private:
     62   void TranslateEthernet();
     63   void TranslateOpenVPN();
     64   void TranslateVPN();
     65   void TranslateWiFiWithState();
     66   void TranslateCellularWithState();
     67   void TranslateNetworkWithState();
     68 
     69   // Creates an ONC object from |dictionary| according to the signature
     70   // associated to |onc_field_name| and adds it to |onc_object_| at
     71   // |onc_field_name|.
     72   void TranslateAndAddNestedObject(const std::string& onc_field_name,
     73                                    const base::DictionaryValue& dictionary);
     74 
     75   // Creates an ONC object from |shill_dictionary_| according to the signature
     76   // associated to |onc_field_name| and adds it to |onc_object_| at
     77   // |onc_field_name|.
     78   void TranslateAndAddNestedObject(const std::string& onc_field_name);
     79 
     80   // Translates a list of nested objects and adds the list to |onc_object_| at
     81   // |onc_field_name|. If there are errors while parsing individual objects or
     82   // if the resulting list contains no entries, the result will not be added to
     83   // |onc_object_|.
     84   void TranslateAndAddListOfObjects(const std::string& onc_field_name,
     85                                     const base::ListValue& list);
     86 
     87   // Applies function CopyProperty to each field of |value_signature| and its
     88   // base signatures.
     89   void CopyPropertiesAccordingToSignature(
     90       const OncValueSignature* value_signature);
     91 
     92   // Applies function CopyProperty to each field of |onc_signature_| and its
     93   // base signatures.
     94   void CopyPropertiesAccordingToSignature();
     95 
     96   // If |shill_property_name| is defined in |field_signature|, copies this
     97   // entry from |shill_dictionary_| to |onc_object_| if it exists.
     98   void CopyProperty(const OncFieldSignature* field_signature);
     99 
    100   // If existent, translates the entry at |shill_property_name| in
    101   // |shill_dictionary_| using |table|. It is an error if no matching table
    102   // entry is found. Writes the result as entry at |onc_field_name| in
    103   // |onc_object_|.
    104   void TranslateWithTableAndSet(const std::string& shill_property_name,
    105                                 const StringTranslationEntry table[],
    106                                 const std::string& onc_field_name);
    107 
    108   const base::DictionaryValue* shill_dictionary_;
    109   const OncValueSignature* onc_signature_;
    110   const FieldTranslationEntry* field_translation_table_;
    111   scoped_ptr<base::DictionaryValue> onc_object_;
    112 
    113   DISALLOW_COPY_AND_ASSIGN(ShillToONCTranslator);
    114 };
    115 
    116 scoped_ptr<base::DictionaryValue>
    117 ShillToONCTranslator::CreateTranslatedONCObject() {
    118   onc_object_.reset(new base::DictionaryValue);
    119   if (onc_signature_ == &kNetworkWithStateSignature) {
    120     TranslateNetworkWithState();
    121   } else if (onc_signature_ == &kEthernetSignature) {
    122     TranslateEthernet();
    123   } else if (onc_signature_ == &kVPNSignature) {
    124     TranslateVPN();
    125   } else if (onc_signature_ == &kOpenVPNSignature) {
    126     TranslateOpenVPN();
    127   } else if (onc_signature_ == &kWiFiWithStateSignature) {
    128     TranslateWiFiWithState();
    129   } else if (onc_signature_ == &kCellularWithStateSignature) {
    130     TranslateCellularWithState();
    131   } else {
    132     CopyPropertiesAccordingToSignature();
    133   }
    134   return onc_object_.Pass();
    135 }
    136 
    137 void ShillToONCTranslator::TranslateEthernet() {
    138   std::string shill_network_type;
    139   shill_dictionary_->GetStringWithoutPathExpansion(shill::kTypeProperty,
    140                                                    &shill_network_type);
    141   const char* onc_auth = ::onc::ethernet::kNone;
    142   if (shill_network_type == shill::kTypeEthernetEap)
    143     onc_auth = ::onc::ethernet::k8021X;
    144   onc_object_->SetStringWithoutPathExpansion(::onc::ethernet::kAuthentication,
    145                                              onc_auth);
    146 }
    147 
    148 void ShillToONCTranslator::TranslateOpenVPN() {
    149   if (shill_dictionary_->HasKey(shill::kOpenVPNVerifyX509NameProperty))
    150     TranslateAndAddNestedObject(::onc::openvpn::kVerifyX509);
    151 
    152   // Shill supports only one RemoteCertKU but ONC requires a list. If existing,
    153   // wraps the value into a list.
    154   std::string certKU;
    155   if (shill_dictionary_->GetStringWithoutPathExpansion(
    156           shill::kOpenVPNRemoteCertKUProperty, &certKU)) {
    157     scoped_ptr<base::ListValue> certKUs(new base::ListValue);
    158     certKUs->AppendString(certKU);
    159     onc_object_->SetWithoutPathExpansion(::onc::openvpn::kRemoteCertKU,
    160                                          certKUs.release());
    161   }
    162 
    163   for (const OncFieldSignature* field_signature = onc_signature_->fields;
    164        field_signature->onc_field_name != NULL; ++field_signature) {
    165     const std::string& onc_field_name = field_signature->onc_field_name;
    166     if (onc_field_name == ::onc::vpn::kSaveCredentials ||
    167         onc_field_name == ::onc::openvpn::kRemoteCertKU ||
    168         onc_field_name == ::onc::openvpn::kServerCAPEMs) {
    169       CopyProperty(field_signature);
    170       continue;
    171     }
    172 
    173     std::string shill_property_name;
    174     const base::Value* shill_value = NULL;
    175     if (!field_translation_table_ ||
    176         !GetShillPropertyName(field_signature->onc_field_name,
    177                               field_translation_table_,
    178                               &shill_property_name) ||
    179         !shill_dictionary_->GetWithoutPathExpansion(shill_property_name,
    180                                                     &shill_value)) {
    181       continue;
    182     }
    183 
    184     scoped_ptr<base::Value> translated;
    185     std::string shill_str;
    186     if (shill_value->GetAsString(&shill_str)) {
    187       // Shill wants all Provider/VPN fields to be strings. Translates these
    188       // strings back to the correct ONC type.
    189       translated = ConvertStringToValue(
    190           shill_str,
    191           field_signature->value_signature->onc_type);
    192 
    193       if (translated.get() == NULL) {
    194         LOG(ERROR) << "Shill property '" << shill_property_name
    195                    << "' with value " << *shill_value
    196                    << " couldn't be converted to base::Value::Type "
    197                    << field_signature->value_signature->onc_type;
    198       } else {
    199         onc_object_->SetWithoutPathExpansion(onc_field_name,
    200                                              translated.release());
    201       }
    202     } else {
    203       LOG(ERROR) << "Shill property '" << shill_property_name
    204                  << "' has value " << *shill_value
    205                  << ", but expected a string";
    206     }
    207   }
    208 }
    209 
    210 void ShillToONCTranslator::TranslateVPN() {
    211   TranslateWithTableAndSet(
    212       shill::kProviderTypeProperty, kVPNTypeTable, ::onc::vpn::kType);
    213   CopyPropertiesAccordingToSignature();
    214 
    215   std::string vpn_type;
    216   if (onc_object_->GetStringWithoutPathExpansion(::onc::vpn::kType,
    217                                                  &vpn_type)) {
    218     if (vpn_type == ::onc::vpn::kTypeL2TP_IPsec) {
    219       TranslateAndAddNestedObject(::onc::vpn::kIPsec);
    220       TranslateAndAddNestedObject(::onc::vpn::kL2TP);
    221     } else {
    222       TranslateAndAddNestedObject(vpn_type);
    223     }
    224   }
    225 }
    226 
    227 void ShillToONCTranslator::TranslateWiFiWithState() {
    228   TranslateWithTableAndSet(
    229       shill::kSecurityProperty, kWiFiSecurityTable, ::onc::wifi::kSecurity);
    230   std::string ssid = shill_property_util::GetSSIDFromProperties(
    231       *shill_dictionary_, NULL /* ignore unknown encoding */);
    232   if (!ssid.empty())
    233     onc_object_->SetStringWithoutPathExpansion(::onc::wifi::kSSID, ssid);
    234   CopyPropertiesAccordingToSignature();
    235 }
    236 
    237 void ShillToONCTranslator::TranslateCellularWithState() {
    238   CopyPropertiesAccordingToSignature();
    239   const base::DictionaryValue* dictionary = NULL;
    240   if (shill_dictionary_->GetDictionaryWithoutPathExpansion(
    241         shill::kServingOperatorProperty, &dictionary)) {
    242     TranslateAndAddNestedObject(::onc::cellular::kServingOperator, *dictionary);
    243   }
    244   if (shill_dictionary_->GetDictionaryWithoutPathExpansion(
    245         shill::kCellularApnProperty, &dictionary)) {
    246     TranslateAndAddNestedObject(::onc::cellular::kAPN, *dictionary);
    247   }
    248   const base::ListValue* list = NULL;
    249   if (shill_dictionary_->GetListWithoutPathExpansion(
    250           shill::kCellularApnListProperty, &list)) {
    251     TranslateAndAddListOfObjects(::onc::cellular::kAPNList, *list);
    252   }
    253 }
    254 
    255 void ShillToONCTranslator::TranslateNetworkWithState() {
    256   CopyPropertiesAccordingToSignature();
    257 
    258   std::string shill_network_type;
    259   shill_dictionary_->GetStringWithoutPathExpansion(shill::kTypeProperty,
    260                                                    &shill_network_type);
    261   std::string onc_network_type = ::onc::network_type::kEthernet;
    262   if (shill_network_type != shill::kTypeEthernet &&
    263       shill_network_type != shill::kTypeEthernetEap) {
    264     TranslateStringToONC(
    265         kNetworkTypeTable, shill_network_type, &onc_network_type);
    266   }
    267   if (!onc_network_type.empty()) {
    268     onc_object_->SetStringWithoutPathExpansion(::onc::network_config::kType,
    269                                                onc_network_type);
    270     TranslateAndAddNestedObject(onc_network_type);
    271   }
    272 
    273   // Since Name is a read only field in Shill unless it's a VPN, it is copied
    274   // here, but not when going the other direction (if it's not a VPN).
    275   std::string name;
    276   shill_dictionary_->GetStringWithoutPathExpansion(shill::kNameProperty,
    277                                                    &name);
    278   onc_object_->SetStringWithoutPathExpansion(::onc::network_config::kName,
    279                                              name);
    280 
    281   std::string state;
    282   if (shill_dictionary_->GetStringWithoutPathExpansion(shill::kStateProperty,
    283                                                        &state)) {
    284     std::string onc_state = ::onc::connection_state::kNotConnected;
    285     if (NetworkState::StateIsConnected(state)) {
    286       onc_state = ::onc::connection_state::kConnected;
    287     } else if (NetworkState::StateIsConnecting(state)) {
    288       onc_state = ::onc::connection_state::kConnecting;
    289     }
    290     onc_object_->SetStringWithoutPathExpansion(
    291         ::onc::network_config::kConnectionState, onc_state);
    292   }
    293 }
    294 
    295 void ShillToONCTranslator::TranslateAndAddNestedObject(
    296     const std::string& onc_field_name) {
    297   TranslateAndAddNestedObject(onc_field_name, *shill_dictionary_);
    298 }
    299 
    300 void ShillToONCTranslator::TranslateAndAddNestedObject(
    301     const std::string& onc_field_name,
    302     const base::DictionaryValue& dictionary) {
    303   const OncFieldSignature* field_signature =
    304       GetFieldSignature(*onc_signature_, onc_field_name);
    305   ShillToONCTranslator nested_translator(dictionary,
    306                                          *field_signature->value_signature);
    307   scoped_ptr<base::DictionaryValue> nested_object =
    308       nested_translator.CreateTranslatedONCObject();
    309   if (nested_object->empty())
    310     return;
    311   onc_object_->SetWithoutPathExpansion(onc_field_name, nested_object.release());
    312 }
    313 
    314 void ShillToONCTranslator::TranslateAndAddListOfObjects(
    315     const std::string& onc_field_name,
    316     const base::ListValue& list) {
    317   const OncFieldSignature* field_signature =
    318       GetFieldSignature(*onc_signature_, onc_field_name);
    319   if (field_signature->value_signature->onc_type != Value::TYPE_LIST) {
    320     LOG(ERROR) << "ONC Field name: '" << onc_field_name << "' has type '"
    321                << field_signature->value_signature->onc_type
    322                << "', expected: base::Value::TYPE_LIST.";
    323     return;
    324   }
    325   DCHECK(field_signature->value_signature->onc_array_entry_signature);
    326   scoped_ptr<base::ListValue> result(new base::ListValue());
    327   for (base::ListValue::const_iterator it = list.begin();
    328        it != list.end(); ++it) {
    329     const base::DictionaryValue* shill_value = NULL;
    330     if (!(*it)->GetAsDictionary(&shill_value))
    331       continue;
    332     ShillToONCTranslator nested_translator(
    333         *shill_value,
    334         *field_signature->value_signature->onc_array_entry_signature);
    335     scoped_ptr<base::DictionaryValue> nested_object =
    336         nested_translator.CreateTranslatedONCObject();
    337     if (nested_object->empty())
    338       // The nested object couldn't be parsed, so simply omit it.
    339       continue;
    340     result->Append(nested_object.release());
    341   }
    342   if (result->empty())
    343     // There are no entries in the list, so there is no need to expose this
    344     // field.
    345     return;
    346   onc_object_->SetWithoutPathExpansion(onc_field_name, result.release());
    347 }
    348 
    349 void ShillToONCTranslator::CopyPropertiesAccordingToSignature() {
    350   CopyPropertiesAccordingToSignature(onc_signature_);
    351 }
    352 
    353 void ShillToONCTranslator::CopyPropertiesAccordingToSignature(
    354     const OncValueSignature* value_signature) {
    355   if (value_signature->base_signature)
    356     CopyPropertiesAccordingToSignature(value_signature->base_signature);
    357   for (const OncFieldSignature* field_signature = value_signature->fields;
    358        field_signature->onc_field_name != NULL; ++field_signature) {
    359     CopyProperty(field_signature);
    360   }
    361 }
    362 
    363 void ShillToONCTranslator::CopyProperty(
    364     const OncFieldSignature* field_signature) {
    365   std::string shill_property_name;
    366   const base::Value* shill_value = NULL;
    367   if (!field_translation_table_ ||
    368       !GetShillPropertyName(field_signature->onc_field_name,
    369                             field_translation_table_,
    370                             &shill_property_name) ||
    371       !shill_dictionary_->GetWithoutPathExpansion(shill_property_name,
    372                                                   &shill_value)) {
    373     return;
    374   }
    375 
    376   if (shill_value->GetType() != field_signature->value_signature->onc_type) {
    377     LOG(ERROR) << "Shill property '" << shill_property_name
    378                << "' with value " << *shill_value
    379                << " has base::Value::Type " << shill_value->GetType()
    380                << " but ONC field '" << field_signature->onc_field_name
    381                << "' requires type "
    382                << field_signature->value_signature->onc_type << ".";
    383     return;
    384   }
    385 
    386  onc_object_->SetWithoutPathExpansion(field_signature->onc_field_name,
    387                                       shill_value->DeepCopy());
    388 }
    389 
    390 void ShillToONCTranslator::TranslateWithTableAndSet(
    391     const std::string& shill_property_name,
    392     const StringTranslationEntry table[],
    393     const std::string& onc_field_name) {
    394   std::string shill_value;
    395   if (!shill_dictionary_->GetStringWithoutPathExpansion(shill_property_name,
    396                                                         &shill_value)) {
    397     return;
    398   }
    399   std::string onc_value;
    400   if (TranslateStringToONC(table, shill_value, &onc_value)) {
    401     onc_object_->SetStringWithoutPathExpansion(onc_field_name, onc_value);
    402     return;
    403   }
    404   LOG(ERROR) << "Shill property '" << shill_property_name << "' with value "
    405              << shill_value << " couldn't be translated to ONC";
    406 }
    407 
    408 }  // namespace
    409 
    410 scoped_ptr<base::DictionaryValue> TranslateShillServiceToONCPart(
    411     const base::DictionaryValue& shill_dictionary,
    412     const OncValueSignature* onc_signature) {
    413   CHECK(onc_signature != NULL);
    414 
    415   ShillToONCTranslator translator(shill_dictionary, *onc_signature);
    416   return translator.CreateTranslatedONCObject();
    417 }
    418 
    419 }  // namespace onc
    420 }  // namespace chromeos
    421