| // Copyright 2014 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "components/sync_wifi/wifi_credential_syncable_service.h" |
| |
| #include <stdint.h> |
| |
| #include <utility> |
| #include <vector> |
| |
| #include "base/logging.h" |
| #include "components/sync/model/sync_change.h" |
| #include "components/sync/model/sync_data.h" |
| #include "components/sync/model/sync_error.h" |
| #include "components/sync/model/sync_error_factory.h" |
| #include "components/sync/model/sync_merge_result.h" |
| #include "components/sync/protocol/sync.pb.h" |
| #include "components/sync_wifi/wifi_credential.h" |
| #include "components/sync_wifi/wifi_security_class.h" |
| |
| namespace sync_wifi { |
| |
| namespace { |
| |
| struct RawCredentialData { |
| std::vector<uint8_t> ssid; |
| WifiSecurityClass security_class; |
| std::string passphrase; |
| }; |
| |
| void BuildSpecifics(const WifiCredential& credential, |
| sync_pb::EntitySpecifics* out_buffer) { |
| DCHECK(out_buffer); |
| sync_pb::WifiCredentialSpecifics* credential_specifics = |
| out_buffer->mutable_wifi_credential(); |
| DCHECK(credential_specifics); |
| credential_specifics->set_ssid(credential.ssid().data(), |
| credential.ssid().size()); |
| credential_specifics->set_security_class( |
| WifiSecurityClassToSyncSecurityClass(credential.security_class())); |
| if (WifiSecurityClassSupportsPassphrases(credential.security_class())) { |
| credential_specifics->set_passphrase(credential.passphrase().data(), |
| credential.passphrase().size()); |
| } |
| } |
| |
| bool ParseSpecifics(const sync_pb::EntitySpecifics& specifics, |
| RawCredentialData* raw_credential) { |
| DCHECK(raw_credential); |
| if (!specifics.has_wifi_credential()) { |
| LOG(ERROR) << "Specifics with missing wifi_credential; skipping"; |
| return false; |
| } |
| |
| const sync_pb::WifiCredentialSpecifics& credential_specifics = |
| specifics.wifi_credential(); |
| if (!credential_specifics.has_ssid()) { |
| LOG(ERROR) << "Specifics with missing SSID; skipping"; |
| return false; |
| } |
| if (!credential_specifics.has_security_class()) { |
| LOG(ERROR) << "Specifics with missing security class; skipping"; |
| return false; |
| } |
| |
| const WifiSecurityClass security_class = |
| WifiSecurityClassFromSyncSecurityClass( |
| credential_specifics.security_class()); |
| if (WifiSecurityClassSupportsPassphrases(security_class) && |
| !credential_specifics.has_passphrase()) { |
| LOG(ERROR) << "Specifics for security class " |
| << credential_specifics.security_class() |
| << " is missing passphrase; skipping"; |
| return false; |
| } |
| |
| raw_credential->ssid.assign(credential_specifics.ssid().begin(), |
| credential_specifics.ssid().end()); |
| raw_credential->security_class = security_class; |
| raw_credential->passphrase = credential_specifics.passphrase(); |
| return true; |
| } |
| |
| // TODO(quiche): Separate SyncData validation from parsing of |
| // WifiCredentialSpecifics. |
| bool ParseSyncData(const syncer::SyncData& sync_data, |
| RawCredentialData* raw_credential) { |
| DCHECK(raw_credential); |
| if (!sync_data.IsValid()) { |
| LOG(WARNING) << "Invalid SyncData; skipping item"; |
| return false; |
| } |
| |
| if (sync_data.GetDataType() != syncer::WIFI_CREDENTIALS) { |
| LOG(WARNING) << "Unexpected SyncData of type " |
| << syncer::ModelTypeToString(sync_data.GetDataType()) |
| << "; skipping item"; |
| return false; |
| } |
| |
| return ParseSpecifics(sync_data.GetSpecifics(), raw_credential); |
| } |
| |
| } // namespace |
| |
| const syncer::ModelType WifiCredentialSyncableService::kModelType = |
| syncer::WIFI_CREDENTIALS; |
| |
| WifiCredentialSyncableService::WifiCredentialSyncableService( |
| std::unique_ptr<WifiConfigDelegate> network_config_delegate) |
| : network_config_delegate_(std::move(network_config_delegate)) { |
| DCHECK(network_config_delegate_); |
| } |
| |
| WifiCredentialSyncableService::~WifiCredentialSyncableService() {} |
| |
| syncer::SyncMergeResult WifiCredentialSyncableService::MergeDataAndStartSyncing( |
| syncer::ModelType type, |
| const syncer::SyncDataList& initial_sync_data, |
| std::unique_ptr<syncer::SyncChangeProcessor> sync_processor, |
| std::unique_ptr<syncer::SyncErrorFactory> /* error_handler */) { |
| DCHECK(!sync_processor_.get()); |
| DCHECK(sync_processor.get()); |
| DCHECK_EQ(kModelType, type); |
| |
| sync_processor_ = std::move(sync_processor); |
| |
| // TODO(quiche): Update local WiFi configuration from |initial_sync_data|. |
| // TODO(quiche): Notify upper layers that sync is ready. |
| NOTIMPLEMENTED(); |
| |
| return syncer::SyncMergeResult(type); |
| } |
| |
| void WifiCredentialSyncableService::StopSyncing(syncer::ModelType type) { |
| DCHECK_EQ(kModelType, type); |
| sync_processor_.reset(); |
| } |
| |
| syncer::SyncDataList WifiCredentialSyncableService::GetAllSyncData( |
| syncer::ModelType type) const { |
| DCHECK_EQ(kModelType, type); |
| NOTIMPLEMENTED(); |
| return syncer::SyncDataList(); |
| } |
| |
| syncer::SyncError WifiCredentialSyncableService::ProcessSyncChanges( |
| const base::Location& /* caller_location */, |
| const syncer::SyncChangeList& change_list) { |
| if (!sync_processor_.get()) { |
| return syncer::SyncError( |
| FROM_HERE, syncer::SyncError::UNREADY_ERROR, |
| "ProcessSyncChanges called before MergeDataAndStartSyncing", |
| kModelType); |
| } |
| |
| for (const syncer::SyncChange& sync_change : change_list) { |
| DCHECK(sync_change.IsValid()); |
| RawCredentialData raw_credential; |
| if (!ParseSyncData(sync_change.sync_data(), &raw_credential)) { |
| LOG(WARNING) << "Failed to parse item; skipping " |
| << syncer::SyncChange::ChangeTypeToString( |
| sync_change.change_type()); |
| continue; |
| } |
| |
| std::unique_ptr<WifiCredential> credential; |
| switch (sync_change.change_type()) { |
| case syncer::SyncChange::ACTION_ADD: |
| credential = WifiCredential::Create(raw_credential.ssid, |
| raw_credential.security_class, |
| raw_credential.passphrase); |
| if (!credential) |
| LOG(WARNING) << "Failed to create credential; skipping"; |
| else |
| network_config_delegate_->AddToLocalNetworks(*credential); |
| break; |
| case syncer::SyncChange::ACTION_UPDATE: |
| // TODO(quiche): Implement update, and add appropriate tests. |
| NOTIMPLEMENTED(); |
| break; |
| case syncer::SyncChange::ACTION_DELETE: |
| // TODO(quiche): Implement delete, and add appropriate tests. |
| NOTIMPLEMENTED(); |
| break; |
| default: |
| return syncer::SyncError( |
| FROM_HERE, syncer::SyncError::DATATYPE_ERROR, |
| "ProcessSyncChanges given invalid SyncChangeType", kModelType); |
| } |
| } |
| |
| return syncer::SyncError(); |
| } |
| |
| bool WifiCredentialSyncableService::AddToSyncedNetworks( |
| const std::string& item_id, |
| const WifiCredential& credential) { |
| if (!sync_processor_.get()) { |
| // Callers must queue updates until MergeDataAndStartSyncing has |
| // been called on this SyncableService. |
| LOG(WARNING) << "WifiCredentials syncable service is not started."; |
| return false; |
| } |
| |
| const SsidAndSecurityClass network_id(credential.ssid(), |
| credential.security_class()); |
| if (synced_networks_and_passphrases_.find(network_id) != |
| synced_networks_and_passphrases_.end()) { |
| // TODO(quiche): If passphrase has changed, submit this to sync as |
| // an ACTION_UPDATE. crbug.com/431436 |
| return false; |
| } |
| |
| syncer::SyncChangeList change_list; |
| syncer::SyncError sync_error; |
| sync_pb::EntitySpecifics wifi_credential_specifics; |
| BuildSpecifics(credential, &wifi_credential_specifics); |
| change_list.push_back( |
| syncer::SyncChange(FROM_HERE, syncer::SyncChange::ACTION_ADD, |
| syncer::SyncData::CreateLocalData( |
| item_id, item_id, wifi_credential_specifics))); |
| sync_error = sync_processor_->ProcessSyncChanges(FROM_HERE, change_list); |
| if (sync_error.IsSet()) { |
| LOG(ERROR) << sync_error.ToString(); |
| return false; |
| } |
| |
| synced_networks_and_passphrases_[network_id] = credential.passphrase(); |
| return true; |
| } |
| |
| } // namespace sync_wifi |