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/strings/string_util.h"
     14 #include "base/values.h"
     15 #include "chromeos/network/network_profile_handler.h"
     16 #include "chromeos/network/network_state.h"
     17 #include "chromeos/network/network_util.h"
     18 #include "chromeos/network/onc/onc_signature.h"
     19 #include "chromeos/network/onc/onc_translation_tables.h"
     20 #include "chromeos/network/shill_property_util.h"
     21 #include "components/onc/onc_constants.h"
     22 #include "third_party/cros_system_api/dbus/service_constants.h"
     23 
     24 namespace chromeos {
     25 namespace onc {
     26 
     27 namespace {
     28 
     29 // Converts |str| to a base::Value of the given |type|. If the conversion fails,
     30 // returns NULL.
     31 scoped_ptr<base::Value> ConvertStringToValue(const std::string& str,
     32                                              base::Value::Type type) {
     33   base::Value* value;
     34   if (type == base::Value::TYPE_STRING) {
     35     value = new base::StringValue(str);
     36   } else {
     37     value = base::JSONReader::Read(str);
     38   }
     39 
     40   if (value == NULL || value->GetType() != type) {
     41     delete value;
     42     value = NULL;
     43   }
     44   return make_scoped_ptr(value);
     45 }
     46 
     47 // This class implements the translation of properties from the given
     48 // |shill_dictionary| to a new ONC object of signature |onc_signature|. Using
     49 // recursive calls to CreateTranslatedONCObject of new instances, nested objects
     50 // are translated.
     51 class ShillToONCTranslator {
     52  public:
     53   ShillToONCTranslator(const base::DictionaryValue& shill_dictionary,
     54                        ::onc::ONCSource onc_source,
     55                        const OncValueSignature& onc_signature)
     56       : shill_dictionary_(&shill_dictionary),
     57         onc_source_(onc_source),
     58         onc_signature_(&onc_signature) {
     59     field_translation_table_ = GetFieldTranslationTable(onc_signature);
     60   }
     61 
     62   ShillToONCTranslator(const base::DictionaryValue& shill_dictionary,
     63                        ::onc::ONCSource onc_source,
     64                        const OncValueSignature& onc_signature,
     65                        const FieldTranslationEntry* field_translation_table)
     66       : shill_dictionary_(&shill_dictionary),
     67         onc_source_(onc_source),
     68         onc_signature_(&onc_signature),
     69         field_translation_table_(field_translation_table) {
     70   }
     71 
     72   // Translates the associated Shill dictionary and creates an ONC object of the
     73   // given signature.
     74   scoped_ptr<base::DictionaryValue> CreateTranslatedONCObject();
     75 
     76  private:
     77   void TranslateEthernet();
     78   void TranslateOpenVPN();
     79   void TranslateIPsec();
     80   void TranslateVPN();
     81   void TranslateWiFiWithState();
     82   void TranslateWiMAXWithState();
     83   void TranslateCellularWithState();
     84   void TranslateCellularDevice();
     85   void TranslateNetworkWithState();
     86   void TranslateIPConfig();
     87   void TranslateSavedOrStaticIPConfig(const std::string& nameserver_property);
     88   void TranslateSavedIPConfig();
     89   void TranslateStaticIPConfig();
     90 
     91   // Creates an ONC object from |dictionary| according to the signature
     92   // associated to |onc_field_name| and adds it to |onc_object_| at
     93   // |onc_field_name|.
     94   void TranslateAndAddNestedObject(const std::string& onc_field_name,
     95                                    const base::DictionaryValue& dictionary);
     96 
     97   // Creates an ONC object from |shill_dictionary_| according to the signature
     98   // associated to |onc_field_name| and adds it to |onc_object_| at
     99   // |onc_field_name|.
    100   void TranslateAndAddNestedObject(const std::string& onc_field_name);
    101 
    102   // Sets |onc_field_name| in dictionary |onc_dictionary_name| in |onc_object_|
    103   // to |value| if the dictionary exists.
    104   void SetNestedOncValue(const std::string& onc_dictionary_name,
    105                          const std::string& onc_field_name,
    106                          const base::Value& value);
    107 
    108   // Translates a list of nested objects and adds the list to |onc_object_| at
    109   // |onc_field_name|. If there are errors while parsing individual objects or
    110   // if the resulting list contains no entries, the result will not be added to
    111   // |onc_object_|.
    112   void TranslateAndAddListOfObjects(const std::string& onc_field_name,
    113                                     const base::ListValue& list);
    114 
    115   // Applies function CopyProperty to each field of |value_signature| and its
    116   // base signatures.
    117   void CopyPropertiesAccordingToSignature(
    118       const OncValueSignature* value_signature);
    119 
    120   // Applies function CopyProperty to each field of |onc_signature_| and its
    121   // base signatures.
    122   void CopyPropertiesAccordingToSignature();
    123 
    124   // If |shill_property_name| is defined in |field_signature|, copies this
    125   // entry from |shill_dictionary_| to |onc_object_| if it exists.
    126   void CopyProperty(const OncFieldSignature* field_signature);
    127 
    128   // If existent, translates the entry at |shill_property_name| in
    129   // |shill_dictionary_| using |table|. It is an error if no matching table
    130   // entry is found. Writes the result as entry at |onc_field_name| in
    131   // |onc_object_|.
    132   void TranslateWithTableAndSet(const std::string& shill_property_name,
    133                                 const StringTranslationEntry table[],
    134                                 const std::string& onc_field_name);
    135 
    136   // Returns the name of the Shill service provided in |shill_dictionary_|
    137   // for debugging.
    138   std::string GetName();
    139 
    140   const base::DictionaryValue* shill_dictionary_;
    141   ::onc::ONCSource onc_source_;
    142   const OncValueSignature* onc_signature_;
    143   const FieldTranslationEntry* field_translation_table_;
    144   scoped_ptr<base::DictionaryValue> onc_object_;
    145 
    146   DISALLOW_COPY_AND_ASSIGN(ShillToONCTranslator);
    147 };
    148 
    149 scoped_ptr<base::DictionaryValue>
    150 ShillToONCTranslator::CreateTranslatedONCObject() {
    151   onc_object_.reset(new base::DictionaryValue);
    152   if (onc_signature_ == &kNetworkWithStateSignature) {
    153     TranslateNetworkWithState();
    154   } else if (onc_signature_ == &kEthernetSignature) {
    155     TranslateEthernet();
    156   } else if (onc_signature_ == &kVPNSignature) {
    157     TranslateVPN();
    158   } else if (onc_signature_ == &kOpenVPNSignature) {
    159     TranslateOpenVPN();
    160   } else if (onc_signature_ == &kIPsecSignature) {
    161     TranslateIPsec();
    162   } else if (onc_signature_ == &kWiFiWithStateSignature) {
    163     TranslateWiFiWithState();
    164   } else if (onc_signature_ == &kWiMAXWithStateSignature) {
    165     TranslateWiMAXWithState();
    166   } else if (onc_signature_ == &kCellularWithStateSignature) {
    167     if (field_translation_table_ == kCellularDeviceTable)
    168       TranslateCellularDevice();
    169     else
    170       TranslateCellularWithState();
    171   } else if (onc_signature_ == &kIPConfigSignature) {
    172     TranslateIPConfig();
    173   } else if (onc_signature_ == &kSavedIPConfigSignature) {
    174     TranslateSavedIPConfig();
    175   } else if (onc_signature_ == &kStaticIPConfigSignature) {
    176     TranslateStaticIPConfig();
    177   } else {
    178     CopyPropertiesAccordingToSignature();
    179   }
    180   return onc_object_.Pass();
    181 }
    182 
    183 void ShillToONCTranslator::TranslateEthernet() {
    184   std::string shill_network_type;
    185   shill_dictionary_->GetStringWithoutPathExpansion(shill::kTypeProperty,
    186                                                    &shill_network_type);
    187   const char* onc_auth = ::onc::ethernet::kAuthenticationNone;
    188   if (shill_network_type == shill::kTypeEthernetEap)
    189     onc_auth = ::onc::ethernet::k8021X;
    190   onc_object_->SetStringWithoutPathExpansion(::onc::ethernet::kAuthentication,
    191                                              onc_auth);
    192 }
    193 
    194 void ShillToONCTranslator::TranslateOpenVPN() {
    195   if (shill_dictionary_->HasKey(shill::kOpenVPNVerifyX509NameProperty))
    196     TranslateAndAddNestedObject(::onc::openvpn::kVerifyX509);
    197 
    198   // Shill supports only one RemoteCertKU but ONC requires a list. If existing,
    199   // wraps the value into a list.
    200   std::string certKU;
    201   if (shill_dictionary_->GetStringWithoutPathExpansion(
    202           shill::kOpenVPNRemoteCertKUProperty, &certKU)) {
    203     scoped_ptr<base::ListValue> certKUs(new base::ListValue);
    204     certKUs->AppendString(certKU);
    205     onc_object_->SetWithoutPathExpansion(::onc::openvpn::kRemoteCertKU,
    206                                          certKUs.release());
    207   }
    208 
    209   for (const OncFieldSignature* field_signature = onc_signature_->fields;
    210        field_signature->onc_field_name != NULL; ++field_signature) {
    211     const std::string& onc_field_name = field_signature->onc_field_name;
    212     if (onc_field_name == ::onc::openvpn::kRemoteCertKU ||
    213         onc_field_name == ::onc::openvpn::kServerCAPEMs) {
    214       CopyProperty(field_signature);
    215       continue;
    216     }
    217 
    218     std::string shill_property_name;
    219     const base::Value* shill_value = NULL;
    220     if (!field_translation_table_ ||
    221         !GetShillPropertyName(field_signature->onc_field_name,
    222                               field_translation_table_,
    223                               &shill_property_name) ||
    224         !shill_dictionary_->GetWithoutPathExpansion(shill_property_name,
    225                                                     &shill_value)) {
    226       continue;
    227     }
    228 
    229     scoped_ptr<base::Value> translated;
    230     std::string shill_str;
    231     if (shill_value->GetAsString(&shill_str)) {
    232       // Shill wants all Provider/VPN fields to be strings. Translates these
    233       // strings back to the correct ONC type.
    234       translated = ConvertStringToValue(
    235           shill_str,
    236           field_signature->value_signature->onc_type);
    237 
    238       if (translated.get() == NULL) {
    239         LOG(ERROR) << "Shill property '" << shill_property_name
    240                    << "' with value " << *shill_value
    241                    << " couldn't be converted to base::Value::Type "
    242                    << field_signature->value_signature->onc_type
    243                    << ": " << GetName();
    244       } else {
    245         onc_object_->SetWithoutPathExpansion(onc_field_name,
    246                                              translated.release());
    247       }
    248     } else {
    249       LOG(ERROR) << "Shill property '" << shill_property_name
    250                  << "' has value " << *shill_value
    251                  << ", but expected a string: " << GetName();
    252     }
    253   }
    254 }
    255 
    256 void ShillToONCTranslator::TranslateIPsec() {
    257   CopyPropertiesAccordingToSignature();
    258   if (shill_dictionary_->HasKey(shill::kL2tpIpsecXauthUserProperty))
    259     TranslateAndAddNestedObject(::onc::ipsec::kXAUTH);
    260   std::string client_cert_id;
    261   shill_dictionary_->GetStringWithoutPathExpansion(
    262       shill::kL2tpIpsecClientCertIdProperty, &client_cert_id);
    263   std::string authentication_type =
    264       client_cert_id.empty() ? ::onc::ipsec::kPSK : ::onc::ipsec::kCert;
    265   onc_object_->SetStringWithoutPathExpansion(::onc::ipsec::kAuthenticationType,
    266                                              authentication_type);
    267 }
    268 
    269 void ShillToONCTranslator::TranslateVPN() {
    270   CopyPropertiesAccordingToSignature();
    271 
    272   // Parse Shill Provider dictionary. Note, this may not exist, e.g. if we are
    273   // just translating network state in network_util::TranslateNetworkStateToONC.
    274   const base::DictionaryValue* provider = NULL;
    275   if (!shill_dictionary_->GetDictionaryWithoutPathExpansion(
    276           shill::kProviderProperty, &provider)) {
    277     return;
    278   }
    279   std::string shill_provider_type, onc_provider_type;
    280   provider->GetStringWithoutPathExpansion(shill::kTypeProperty,
    281                                           &shill_provider_type);
    282   if (!TranslateStringToONC(
    283           kVPNTypeTable, shill_provider_type, &onc_provider_type)) {
    284     return;
    285   }
    286   onc_object_->SetStringWithoutPathExpansion(::onc::vpn::kType,
    287                                              onc_provider_type);
    288   std::string provider_host;
    289   if (provider->GetStringWithoutPathExpansion(shill::kHostProperty,
    290                                               &provider_host)) {
    291     onc_object_->SetStringWithoutPathExpansion(::onc::vpn::kHost,
    292                                                provider_host);
    293   }
    294 
    295   // Translate the nested dictionary.
    296   std::string provider_type_dictionary;
    297   if (onc_provider_type == ::onc::vpn::kTypeL2TP_IPsec) {
    298     TranslateAndAddNestedObject(::onc::vpn::kIPsec, *provider);
    299     TranslateAndAddNestedObject(::onc::vpn::kL2TP, *provider);
    300     provider_type_dictionary = ::onc::vpn::kIPsec;
    301   } else {
    302     TranslateAndAddNestedObject(onc_provider_type, *provider);
    303     provider_type_dictionary = onc_provider_type;
    304   }
    305 
    306   bool save_credentials;
    307   if (shill_dictionary_->GetBooleanWithoutPathExpansion(
    308           shill::kSaveCredentialsProperty, &save_credentials)) {
    309     SetNestedOncValue(provider_type_dictionary,
    310                       ::onc::vpn::kSaveCredentials,
    311                       base::FundamentalValue(save_credentials));
    312   }
    313 }
    314 
    315 void ShillToONCTranslator::TranslateWiFiWithState() {
    316   TranslateWithTableAndSet(
    317       shill::kSecurityProperty, kWiFiSecurityTable, ::onc::wifi::kSecurity);
    318   std::string ssid = shill_property_util::GetSSIDFromProperties(
    319       *shill_dictionary_, NULL /* ignore unknown encoding */);
    320   if (!ssid.empty())
    321     onc_object_->SetStringWithoutPathExpansion(::onc::wifi::kSSID, ssid);
    322   CopyPropertiesAccordingToSignature();
    323   TranslateAndAddNestedObject(::onc::wifi::kEAP);
    324 }
    325 
    326 void ShillToONCTranslator::TranslateWiMAXWithState() {
    327   CopyPropertiesAccordingToSignature();
    328   TranslateAndAddNestedObject(::onc::wimax::kEAP);
    329 }
    330 
    331 void ShillToONCTranslator::TranslateCellularWithState() {
    332   CopyPropertiesAccordingToSignature();
    333   TranslateWithTableAndSet(shill::kActivationStateProperty,
    334                            kActivationStateTable,
    335                            ::onc::cellular::kActivationState);
    336   TranslateWithTableAndSet(shill::kRoamingStateProperty,
    337                            kRoamingStateTable,
    338                            ::onc::cellular::kRoamingState);
    339   const base::DictionaryValue* dictionary = NULL;
    340   if (shill_dictionary_->GetDictionaryWithoutPathExpansion(
    341         shill::kServingOperatorProperty, &dictionary)) {
    342     TranslateAndAddNestedObject(::onc::cellular::kServingOperator, *dictionary);
    343   }
    344   if (shill_dictionary_->GetDictionaryWithoutPathExpansion(
    345         shill::kCellularApnProperty, &dictionary)) {
    346     TranslateAndAddNestedObject(::onc::cellular::kAPN, *dictionary);
    347   }
    348   if (shill_dictionary_->GetDictionaryWithoutPathExpansion(
    349         shill::kCellularLastGoodApnProperty, &dictionary)) {
    350     TranslateAndAddNestedObject(::onc::cellular::kLastGoodAPN, *dictionary);
    351   }
    352   // Merge the Device dictionary with this one (Cellular) using the
    353   // CellularDevice signature.
    354   const base::DictionaryValue* device_dictionary = NULL;
    355   if (!shill_dictionary_->GetDictionaryWithoutPathExpansion(
    356           shill::kDeviceProperty, &device_dictionary)) {
    357     return;
    358   }
    359   ShillToONCTranslator nested_translator(*device_dictionary,
    360                                          onc_source_,
    361                                          kCellularWithStateSignature,
    362                                          kCellularDeviceTable);
    363   scoped_ptr<base::DictionaryValue> nested_object =
    364       nested_translator.CreateTranslatedONCObject();
    365   onc_object_->MergeDictionary(nested_object.get());
    366 }
    367 
    368 void ShillToONCTranslator::TranslateCellularDevice() {
    369   CopyPropertiesAccordingToSignature();
    370   const base::DictionaryValue* shill_sim_lock_status = NULL;
    371   if (shill_dictionary_->GetDictionaryWithoutPathExpansion(
    372           shill::kSIMLockStatusProperty, &shill_sim_lock_status)) {
    373     TranslateAndAddNestedObject(::onc::cellular::kSIMLockStatus,
    374                                 *shill_sim_lock_status);
    375   }
    376   const base::ListValue* shill_apns = NULL;
    377   if (shill_dictionary_->GetListWithoutPathExpansion(
    378           shill::kCellularApnListProperty, &shill_apns)) {
    379     TranslateAndAddListOfObjects(::onc::cellular::kAPNList, *shill_apns);
    380   }
    381   const base::ListValue* shill_found_networks = NULL;
    382   if (shill_dictionary_->GetListWithoutPathExpansion(
    383           shill::kFoundNetworksProperty, &shill_found_networks)) {
    384     TranslateAndAddListOfObjects(::onc::cellular::kFoundNetworks,
    385                                  *shill_found_networks);
    386   }
    387 }
    388 
    389 void ShillToONCTranslator::TranslateNetworkWithState() {
    390   CopyPropertiesAccordingToSignature();
    391 
    392   std::string shill_network_type;
    393   shill_dictionary_->GetStringWithoutPathExpansion(shill::kTypeProperty,
    394                                                    &shill_network_type);
    395   std::string onc_network_type = ::onc::network_type::kEthernet;
    396   if (shill_network_type != shill::kTypeEthernet &&
    397       shill_network_type != shill::kTypeEthernetEap) {
    398     TranslateStringToONC(
    399         kNetworkTypeTable, shill_network_type, &onc_network_type);
    400   }
    401   // Translate nested Cellular, WiFi, etc. properties.
    402   if (!onc_network_type.empty()) {
    403     onc_object_->SetStringWithoutPathExpansion(::onc::network_config::kType,
    404                                                onc_network_type);
    405     TranslateAndAddNestedObject(onc_network_type);
    406   }
    407 
    408   // Since Name is a read only field in Shill unless it's a VPN, it is copied
    409   // here, but not when going the other direction (if it's not a VPN).
    410   std::string name;
    411   shill_dictionary_->GetStringWithoutPathExpansion(shill::kNameProperty, &name);
    412   onc_object_->SetStringWithoutPathExpansion(::onc::network_config::kName,
    413                                              name);
    414 
    415   // Limit ONC state to "NotConnected", "Connected", or "Connecting".
    416   std::string state;
    417   if (shill_dictionary_->GetStringWithoutPathExpansion(shill::kStateProperty,
    418                                                        &state)) {
    419     std::string onc_state = ::onc::connection_state::kNotConnected;
    420     if (NetworkState::StateIsConnected(state)) {
    421       onc_state = ::onc::connection_state::kConnected;
    422     } else if (NetworkState::StateIsConnecting(state)) {
    423       onc_state = ::onc::connection_state::kConnecting;
    424     }
    425     onc_object_->SetStringWithoutPathExpansion(
    426         ::onc::network_config::kConnectionState, onc_state);
    427     // Only set 'RestrictedConnectivity' if true.
    428     if (state == shill::kStatePortal) {
    429       onc_object_->SetBooleanWithoutPathExpansion(
    430           ::onc::network_config::kRestrictedConnectivity, true);
    431     }
    432   }
    433 
    434   std::string profile_path;
    435   if (onc_source_ != ::onc::ONC_SOURCE_UNKNOWN &&
    436       shill_dictionary_->GetStringWithoutPathExpansion(shill::kProfileProperty,
    437                                                        &profile_path)) {
    438     std::string source;
    439     if (onc_source_ == ::onc::ONC_SOURCE_DEVICE_POLICY)
    440       source = ::onc::network_config::kSourceDevicePolicy;
    441     else if (onc_source_ == ::onc::ONC_SOURCE_USER_POLICY)
    442       source = ::onc::network_config::kSourceUserPolicy;
    443     else if (profile_path == NetworkProfileHandler::GetSharedProfilePath())
    444       source = ::onc::network_config::kSourceDevice;
    445     else if (!profile_path.empty())
    446       source = ::onc::network_config::kSourceUser;
    447     else
    448       source = ::onc::network_config::kSourceNone;
    449     onc_object_->SetStringWithoutPathExpansion(
    450         ::onc::network_config::kSource, source);
    451   }
    452 
    453   // Use a human-readable aa:bb format for any hardware MAC address. Note:
    454   // this property is provided by the caller but is not part of the Shill
    455   // Service properties (it is copied from the Device properties).
    456   std::string address;
    457   if (shill_dictionary_->GetStringWithoutPathExpansion(shill::kAddressProperty,
    458                                                        &address)) {
    459     onc_object_->SetStringWithoutPathExpansion(
    460         ::onc::network_config::kMacAddress,
    461         network_util::FormattedMacAddress(address));
    462   }
    463 
    464   // Shill's Service has an IPConfig property (note the singular), not an
    465   // IPConfigs property. However, we require the caller of the translation to
    466   // patch the Shill dictionary before passing it to the translator.
    467   const base::ListValue* shill_ipconfigs = NULL;
    468   if (shill_dictionary_->GetListWithoutPathExpansion(shill::kIPConfigsProperty,
    469                                                      &shill_ipconfigs)) {
    470     TranslateAndAddListOfObjects(::onc::network_config::kIPConfigs,
    471                                  *shill_ipconfigs);
    472   }
    473 
    474   TranslateAndAddNestedObject(::onc::network_config::kSavedIPConfig);
    475   TranslateAndAddNestedObject(::onc::network_config::kStaticIPConfig);
    476 }
    477 
    478 void ShillToONCTranslator::TranslateIPConfig() {
    479   CopyPropertiesAccordingToSignature();
    480   std::string shill_ip_method;
    481   shill_dictionary_->GetStringWithoutPathExpansion(shill::kMethodProperty,
    482                                                    &shill_ip_method);
    483   std::string type;
    484   if (shill_ip_method == shill::kTypeIPv4 ||
    485       shill_ip_method == shill::kTypeDHCP) {
    486     type = ::onc::ipconfig::kIPv4;
    487   } else if (shill_ip_method == shill::kTypeIPv6 ||
    488              shill_ip_method == shill::kTypeDHCP6) {
    489     type = ::onc::ipconfig::kIPv6;
    490   } else {
    491     return;  // Ignore unhandled IPConfig types, e.g. bootp, zeroconf, ppp
    492   }
    493 
    494   onc_object_->SetStringWithoutPathExpansion(::onc::ipconfig::kType, type);
    495 }
    496 
    497 void ShillToONCTranslator::TranslateSavedOrStaticIPConfig(
    498     const std::string& nameserver_property) {
    499   CopyPropertiesAccordingToSignature();
    500   // Saved/Static IP config nameservers are stored as a comma separated list.
    501   std::string shill_nameservers;
    502   shill_dictionary_->GetStringWithoutPathExpansion(
    503       nameserver_property, &shill_nameservers);
    504   std::vector<std::string> onc_nameserver_vector;
    505   if (Tokenize(shill_nameservers, ",", &onc_nameserver_vector) > 0) {
    506     scoped_ptr<base::ListValue> onc_nameservers(new base::ListValue);
    507     for (std::vector<std::string>::iterator iter =
    508              onc_nameserver_vector.begin();
    509          iter != onc_nameserver_vector.end(); ++iter) {
    510       onc_nameservers->AppendString(*iter);
    511     }
    512     onc_object_->SetWithoutPathExpansion(::onc::ipconfig::kNameServers,
    513                                          onc_nameservers.release());
    514   }
    515   // Static and Saved IPConfig in Shill are always of type IPv4. Set this type
    516   // in ONC, but not if the object would be empty except the type.
    517   if (!onc_object_->empty()) {
    518     onc_object_->SetStringWithoutPathExpansion(::onc::ipconfig::kType,
    519                                                ::onc::ipconfig::kIPv4);
    520   }
    521 }
    522 
    523 void ShillToONCTranslator::TranslateSavedIPConfig() {
    524   TranslateSavedOrStaticIPConfig(shill::kSavedIPNameServersProperty);
    525 }
    526 
    527 void ShillToONCTranslator::TranslateStaticIPConfig() {
    528   TranslateSavedOrStaticIPConfig(shill::kStaticIPNameServersProperty);
    529 }
    530 
    531 void ShillToONCTranslator::TranslateAndAddNestedObject(
    532     const std::string& onc_field_name) {
    533   TranslateAndAddNestedObject(onc_field_name, *shill_dictionary_);
    534 }
    535 
    536 void ShillToONCTranslator::TranslateAndAddNestedObject(
    537     const std::string& onc_field_name,
    538     const base::DictionaryValue& dictionary) {
    539   const OncFieldSignature* field_signature =
    540       GetFieldSignature(*onc_signature_, onc_field_name);
    541   if (!field_signature) {
    542     NOTREACHED() << "Unable to find signature for field: " << onc_field_name;
    543     return;
    544   }
    545   ShillToONCTranslator nested_translator(
    546       dictionary, onc_source_, *field_signature->value_signature);
    547   scoped_ptr<base::DictionaryValue> nested_object =
    548       nested_translator.CreateTranslatedONCObject();
    549   if (nested_object->empty())
    550     return;
    551   onc_object_->SetWithoutPathExpansion(onc_field_name, nested_object.release());
    552 }
    553 
    554 void ShillToONCTranslator::SetNestedOncValue(
    555     const std::string& onc_dictionary_name,
    556     const std::string& onc_field_name,
    557     const base::Value& value) {
    558   base::DictionaryValue* nested;
    559   if (!onc_object_->GetDictionaryWithoutPathExpansion(
    560           onc_dictionary_name, &nested)) {
    561     nested = new base::DictionaryValue;
    562     onc_object_->SetWithoutPathExpansion(onc_dictionary_name, nested);
    563   }
    564   nested->SetWithoutPathExpansion(onc_field_name, value.DeepCopy());
    565 }
    566 
    567 void ShillToONCTranslator::TranslateAndAddListOfObjects(
    568     const std::string& onc_field_name,
    569     const base::ListValue& list) {
    570   const OncFieldSignature* field_signature =
    571       GetFieldSignature(*onc_signature_, onc_field_name);
    572   if (field_signature->value_signature->onc_type != base::Value::TYPE_LIST) {
    573     LOG(ERROR) << "ONC Field name: '" << onc_field_name << "' has type '"
    574                << field_signature->value_signature->onc_type
    575                << "', expected: base::Value::TYPE_LIST: " << GetName();
    576     return;
    577   }
    578   DCHECK(field_signature->value_signature->onc_array_entry_signature);
    579   scoped_ptr<base::ListValue> result(new base::ListValue());
    580   for (base::ListValue::const_iterator it = list.begin();
    581        it != list.end(); ++it) {
    582     const base::DictionaryValue* shill_value = NULL;
    583     if (!(*it)->GetAsDictionary(&shill_value))
    584       continue;
    585     ShillToONCTranslator nested_translator(
    586         *shill_value,
    587         onc_source_,
    588         *field_signature->value_signature->onc_array_entry_signature);
    589     scoped_ptr<base::DictionaryValue> nested_object =
    590         nested_translator.CreateTranslatedONCObject();
    591     // If the nested object couldn't be parsed, simply omit it.
    592     if (nested_object->empty())
    593       continue;
    594     result->Append(nested_object.release());
    595   }
    596   // If there are no entries in the list, there is no need to expose this field.
    597   if (result->empty())
    598     return;
    599   onc_object_->SetWithoutPathExpansion(onc_field_name, result.release());
    600 }
    601 
    602 void ShillToONCTranslator::CopyPropertiesAccordingToSignature() {
    603   CopyPropertiesAccordingToSignature(onc_signature_);
    604 }
    605 
    606 void ShillToONCTranslator::CopyPropertiesAccordingToSignature(
    607     const OncValueSignature* value_signature) {
    608   if (value_signature->base_signature)
    609     CopyPropertiesAccordingToSignature(value_signature->base_signature);
    610   if (!value_signature->fields)
    611     return;
    612   for (const OncFieldSignature* field_signature = value_signature->fields;
    613        field_signature->onc_field_name != NULL; ++field_signature) {
    614     CopyProperty(field_signature);
    615   }
    616 }
    617 
    618 void ShillToONCTranslator::CopyProperty(
    619     const OncFieldSignature* field_signature) {
    620   std::string shill_property_name;
    621   const base::Value* shill_value = NULL;
    622   if (!field_translation_table_ ||
    623       !GetShillPropertyName(field_signature->onc_field_name,
    624                             field_translation_table_,
    625                             &shill_property_name) ||
    626       !shill_dictionary_->GetWithoutPathExpansion(shill_property_name,
    627                                                   &shill_value)) {
    628     return;
    629   }
    630 
    631   if (shill_value->GetType() != field_signature->value_signature->onc_type) {
    632     LOG(ERROR) << "Shill property '" << shill_property_name
    633                << "' with value " << *shill_value
    634                << " has base::Value::Type " << shill_value->GetType()
    635                << " but ONC field '" << field_signature->onc_field_name
    636                << "' requires type "
    637                << field_signature->value_signature->onc_type
    638                << ": " << GetName();
    639     return;
    640   }
    641 
    642  onc_object_->SetWithoutPathExpansion(field_signature->onc_field_name,
    643                                       shill_value->DeepCopy());
    644 }
    645 
    646 void ShillToONCTranslator::TranslateWithTableAndSet(
    647     const std::string& shill_property_name,
    648     const StringTranslationEntry table[],
    649     const std::string& onc_field_name) {
    650   std::string shill_value;
    651   if (!shill_dictionary_->GetStringWithoutPathExpansion(shill_property_name,
    652                                                         &shill_value)) {
    653     return;
    654   }
    655   std::string onc_value;
    656   if (TranslateStringToONC(table, shill_value, &onc_value)) {
    657     onc_object_->SetStringWithoutPathExpansion(onc_field_name, onc_value);
    658     return;
    659   }
    660   LOG(ERROR) << "Shill property '" << shill_property_name << "' with value "
    661              << shill_value << " couldn't be translated to ONC: " << GetName();
    662 }
    663 
    664 std::string ShillToONCTranslator::GetName() {
    665   DCHECK(shill_dictionary_);
    666   std::string name;
    667   shill_dictionary_->GetStringWithoutPathExpansion(shill::kNameProperty, &name);
    668   return name;
    669 }
    670 
    671 }  // namespace
    672 
    673 scoped_ptr<base::DictionaryValue> TranslateShillServiceToONCPart(
    674     const base::DictionaryValue& shill_dictionary,
    675     ::onc::ONCSource onc_source,
    676     const OncValueSignature* onc_signature) {
    677   CHECK(onc_signature != NULL);
    678 
    679   ShillToONCTranslator translator(shill_dictionary, onc_source, *onc_signature);
    680   return translator.CreateTranslatedONCObject();
    681 }
    682 
    683 }  // namespace onc
    684 }  // namespace chromeos
    685