| Index: sync/engine/model_type_sync_worker_impl.cc
|
| diff --git a/sync/engine/model_type_sync_worker_impl.cc b/sync/engine/model_type_sync_worker_impl.cc
|
| index a5402c20ae42b875482b75f16ab0e98258dd0c97..a58400f456679b388fb4aa3c6bbdb96b3acfa5ee 100644
|
| --- a/sync/engine/model_type_sync_worker_impl.cc
|
| +++ b/sync/engine/model_type_sync_worker_impl.cc
|
| @@ -13,6 +13,7 @@
|
| #include "sync/engine/model_type_sync_proxy.h"
|
| #include "sync/engine/non_blocking_type_commit_contribution.h"
|
| #include "sync/syncable/syncable_util.h"
|
| +#include "sync/util/cryptographer.h"
|
| #include "sync/util/time.h"
|
|
|
| namespace syncer {
|
| @@ -20,11 +21,14 @@ namespace syncer {
|
| ModelTypeSyncWorkerImpl::ModelTypeSyncWorkerImpl(
|
| ModelType type,
|
| const DataTypeState& initial_state,
|
| + const UpdateResponseDataList& saved_pending_updates,
|
| + CryptographerProvider* cryptographer_provider,
|
| NudgeHandler* nudge_handler,
|
| scoped_ptr<ModelTypeSyncProxy> type_sync_proxy)
|
| : type_(type),
|
| data_type_state_(initial_state),
|
| type_sync_proxy_(type_sync_proxy.Pass()),
|
| + cryptographer_provider_(cryptographer_provider),
|
| nudge_handler_(nudge_handler),
|
| entities_deleter_(&entities_),
|
| weak_ptr_factory_(this) {
|
| @@ -32,6 +36,18 @@ ModelTypeSyncWorkerImpl::ModelTypeSyncWorkerImpl(
|
| if (!data_type_state_.initial_sync_done) {
|
| nudge_handler_->NudgeForInitialDownload(type_);
|
| }
|
| +
|
| + for (UpdateResponseDataList::const_iterator it =
|
| + saved_pending_updates.begin();
|
| + it != saved_pending_updates.end();
|
| + ++it) {
|
| + EntityTracker* entity_tracker = EntityTracker::FromServerUpdate(
|
| + it->id, it->client_tag_hash, it->response_version);
|
| + entity_tracker->ReceivePendingUpdate(*it);
|
| + entities_.insert(std::make_pair(it->client_tag_hash, entity_tracker));
|
| + }
|
| +
|
| + TryDecryptPendingUpdates();
|
| }
|
|
|
| ModelTypeSyncWorkerImpl::~ModelTypeSyncWorkerImpl() {
|
| @@ -42,6 +58,33 @@ ModelType ModelTypeSyncWorkerImpl::GetModelType() const {
|
| return type_;
|
| }
|
|
|
| +bool ModelTypeSyncWorkerImpl::IsEncryptionRequired() const {
|
| + return !data_type_state_.encryption_key_name.empty();
|
| +}
|
| +
|
| +void ModelTypeSyncWorkerImpl::SetEncryptionKeyName(const std::string& name) {
|
| + if (data_type_state_.encryption_key_name == name)
|
| + return;
|
| +
|
| + data_type_state_.encryption_key_name = name;
|
| +
|
| + // Pretend to send an update. This will cause the TypeSyncProxy to notice
|
| + // the new encryption key and take appropriate action.
|
| + type_sync_proxy_->OnUpdateReceived(
|
| + data_type_state_, UpdateResponseDataList(), UpdateResponseDataList());
|
| +}
|
| +
|
| +void ModelTypeSyncWorkerImpl::OnCryptographerStateChanged() {
|
| + TryDecryptPendingUpdates();
|
| +
|
| + ScopedCryptographerRef scoped_cryptographer_ref;
|
| + cryptographer_provider_->InitScopedCryptographerRef(
|
| + &scoped_cryptographer_ref);
|
| + Cryptographer* cryptographer = scoped_cryptographer_ref.Get();
|
| + if (CanCommitItems(cryptographer))
|
| + nudge_handler_->NudgeForCommit(type_);
|
| +}
|
| +
|
| // UpdateHandler implementation.
|
| void ModelTypeSyncWorkerImpl::GetDownloadProgress(
|
| sync_pb::DataTypeProgressMarker* progress_marker) const {
|
| @@ -66,7 +109,14 @@ SyncerError ModelTypeSyncWorkerImpl::ProcessGetUpdatesResponse(
|
| data_type_state_.type_context = mutated_context;
|
| data_type_state_.progress_marker = progress_marker;
|
|
|
| + ScopedCryptographerRef scoped_cryptographer_ref;
|
| + cryptographer_provider_->InitScopedCryptographerRef(
|
| + &scoped_cryptographer_ref);
|
| + Cryptographer* cryptographer = scoped_cryptographer_ref.Get();
|
| + DCHECK(cryptographer);
|
| +
|
| UpdateResponseDataList response_datas;
|
| + UpdateResponseDataList pending_updates;
|
|
|
| for (SyncEntityList::const_iterator update_it = applicable_updates.begin();
|
| update_it != applicable_updates.end();
|
| @@ -86,16 +136,17 @@ SyncerError ModelTypeSyncWorkerImpl::ProcessGetUpdatesResponse(
|
| const std::string& client_tag_hash =
|
| update_entity->client_defined_unique_tag();
|
| DCHECK(!client_tag_hash.empty());
|
| +
|
| + EntityTracker* entity_tracker = NULL;
|
| EntityMap::const_iterator map_it = entities_.find(client_tag_hash);
|
| if (map_it == entities_.end()) {
|
| - EntityTracker* entity =
|
| + entity_tracker =
|
| EntityTracker::FromServerUpdate(update_entity->id_string(),
|
| client_tag_hash,
|
| update_entity->version());
|
| - entities_.insert(std::make_pair(client_tag_hash, entity));
|
| + entities_.insert(std::make_pair(client_tag_hash, entity_tracker));
|
| } else {
|
| - EntityTracker* entity = map_it->second;
|
| - entity->ReceiveUpdate(update_entity->version());
|
| + entity_tracker = map_it->second;
|
| }
|
|
|
| // Prepare the message for the model thread.
|
| @@ -107,14 +158,39 @@ SyncerError ModelTypeSyncWorkerImpl::ProcessGetUpdatesResponse(
|
| response_data.mtime = ProtoTimeToTime(update_entity->mtime());
|
| response_data.non_unique_name = update_entity->name();
|
| response_data.deleted = update_entity->deleted();
|
| - response_data.specifics = update_entity->specifics();
|
|
|
| - response_datas.push_back(response_data);
|
| + const sync_pb::EntitySpecifics& specifics = update_entity->specifics();
|
| +
|
| + if (!specifics.has_encrypted()) {
|
| + // No encryption.
|
| + entity_tracker->ReceiveUpdate(update_entity->version());
|
| + response_data.specifics = specifics;
|
| + response_datas.push_back(response_data);
|
| + } else if (specifics.has_encrypted() &&
|
| + cryptographer->CanDecrypt(specifics.encrypted())) {
|
| + // Encrypted, but we know the key.
|
| + if (DecryptSpecifics(
|
| + cryptographer, specifics, &response_data.specifics)) {
|
| + entity_tracker->ReceiveUpdate(update_entity->version());
|
| + response_data.encryption_key_name = specifics.encrypted().key_name();
|
| + response_datas.push_back(response_data);
|
| + }
|
| + } else if (specifics.has_encrypted() &&
|
| + !cryptographer->CanDecrypt(specifics.encrypted())) {
|
| + // Can't decrypt right now. Ask the entity tracker to handle it.
|
| + response_data.specifics = specifics;
|
| + if (entity_tracker->ReceivePendingUpdate(response_data)) {
|
| + // Send to the model thread for safe-keeping across restarts if the
|
| + // tracker decides the update is worth keeping.
|
| + pending_updates.push_back(response_data);
|
| + }
|
| + }
|
| }
|
| }
|
|
|
| // Forward these updates to the model thread so it can do the rest.
|
| - type_sync_proxy_->OnUpdateReceived(data_type_state_, response_datas);
|
| + type_sync_proxy_->OnUpdateReceived(
|
| + data_type_state_, response_datas, pending_updates);
|
|
|
| return SYNCER_OK;
|
| }
|
| @@ -128,8 +204,8 @@ void ModelTypeSyncWorkerImpl::ApplyUpdates(sessions::StatusController* status) {
|
| if (!data_type_state_.initial_sync_done) {
|
| data_type_state_.initial_sync_done = true;
|
|
|
| - UpdateResponseDataList empty_update_list;
|
| - type_sync_proxy_->OnUpdateReceived(data_type_state_, empty_update_list);
|
| + type_sync_proxy_->OnUpdateReceived(
|
| + data_type_state_, UpdateResponseDataList(), UpdateResponseDataList());
|
| }
|
| }
|
|
|
| @@ -144,7 +220,7 @@ void ModelTypeSyncWorkerImpl::EnqueueForCommit(
|
| const CommitRequestDataList& list) {
|
| DCHECK(CalledOnValidThread());
|
|
|
| - DCHECK(CanCommitItems())
|
| + DCHECK(IsTypeInitialized())
|
| << "Asked to commit items before type was initialized. "
|
| << "ModelType is: " << ModelTypeToString(type_);
|
|
|
| @@ -153,6 +229,13 @@ void ModelTypeSyncWorkerImpl::EnqueueForCommit(
|
| ++it) {
|
| StorePendingCommit(*it);
|
| }
|
| +
|
| + ScopedCryptographerRef scoped_cryptographer_ref;
|
| + cryptographer_provider_->InitScopedCryptographerRef(
|
| + &scoped_cryptographer_ref);
|
| + Cryptographer* cryptographer = scoped_cryptographer_ref.Get();
|
| + if (CanCommitItems(cryptographer))
|
| + nudge_handler_->NudgeForCommit(type_);
|
| }
|
|
|
| // CommitContributor implementation.
|
| @@ -164,7 +247,12 @@ scoped_ptr<CommitContribution> ModelTypeSyncWorkerImpl::GetContribution(
|
| std::vector<int64> sequence_numbers;
|
| google::protobuf::RepeatedPtrField<sync_pb::SyncEntity> commit_entities;
|
|
|
| - if (!CanCommitItems())
|
| + ScopedCryptographerRef scoped_cryptographer_ref;
|
| + cryptographer_provider_->InitScopedCryptographerRef(
|
| + &scoped_cryptographer_ref);
|
| + Cryptographer* cryptographer = scoped_cryptographer_ref.Get();
|
| +
|
| + if (!CanCommitItems(cryptographer))
|
| return scoped_ptr<CommitContribution>();
|
|
|
| // TODO(rlarocque): Avoid iterating here.
|
| @@ -177,7 +265,7 @@ scoped_ptr<CommitContribution> ModelTypeSyncWorkerImpl::GetContribution(
|
| int64 sequence_number = -1;
|
|
|
| entity->PrepareCommitProto(commit_entity, &sequence_number);
|
| - HelpInitializeCommitEntity(commit_entity);
|
| + HelpInitializeCommitEntity(cryptographer, commit_entity);
|
| sequence_numbers.push_back(sequence_number);
|
|
|
| space_remaining--;
|
| @@ -222,9 +310,6 @@ void ModelTypeSyncWorkerImpl::StorePendingCommit(
|
| request.deleted,
|
| request.specifics);
|
| }
|
| -
|
| - if (CanCommitItems())
|
| - nudge_handler_->NudgeForCommit(type_);
|
| }
|
|
|
| void ModelTypeSyncWorkerImpl::OnCommitResponse(
|
| @@ -260,14 +345,30 @@ base::WeakPtr<ModelTypeSyncWorkerImpl> ModelTypeSyncWorkerImpl::AsWeakPtr() {
|
| return weak_ptr_factory_.GetWeakPtr();
|
| }
|
|
|
| -bool ModelTypeSyncWorkerImpl::CanCommitItems() const {
|
| - // We can't commit anything until we know the type's parent node.
|
| - // We'll get it in the first update response.
|
| +bool ModelTypeSyncWorkerImpl::IsTypeInitialized() const {
|
| return !data_type_state_.type_root_id.empty() &&
|
| data_type_state_.initial_sync_done;
|
| }
|
|
|
| +bool ModelTypeSyncWorkerImpl::CanCommitItems(
|
| + Cryptographer* cryptographer) const {
|
| + // We can't commit anything until we know the type's parent node.
|
| + // We'll get it in the first update response.
|
| + if (!IsTypeInitialized())
|
| + return false;
|
| +
|
| + // Don't commit if we should be encrypting but don't have the required keys.
|
| + if (IsEncryptionRequired() && (!cryptographer || !cryptographer->is_ready() ||
|
| + cryptographer->GetDefaultNigoriKeyName() !=
|
| + data_type_state_.encryption_key_name)) {
|
| + return false;
|
| + }
|
| +
|
| + return true;
|
| +}
|
| +
|
| void ModelTypeSyncWorkerImpl::HelpInitializeCommitEntity(
|
| + Cryptographer* cryptographer,
|
| sync_pb::SyncEntity* sync_entity) {
|
| // Initial commits need our help to generate a client ID.
|
| if (!sync_entity->has_id_string()) {
|
| @@ -277,14 +378,80 @@ void ModelTypeSyncWorkerImpl::HelpInitializeCommitEntity(
|
| base::StringPrintf("%s-%" PRId64, ModelTypeToString(type_), id));
|
| }
|
|
|
| + // Encrypt the specifics and hide the title if necessary.
|
| + if (IsEncryptionRequired()) {
|
| + sync_pb::EntitySpecifics encrypted_specifics;
|
| + cryptographer->Encrypt(sync_entity->specifics(),
|
| + encrypted_specifics.mutable_encrypted());
|
| + sync_entity->mutable_specifics()->CopyFrom(encrypted_specifics);
|
| + sync_entity->set_name("encrypted");
|
| + }
|
| +
|
| // Always include enough specifics to identify the type. Do this even in
|
| // deletion requests, where the specifics are otherwise invalid.
|
| - if (!sync_entity->has_specifics()) {
|
| - AddDefaultFieldValue(type_, sync_entity->mutable_specifics());
|
| - }
|
| + AddDefaultFieldValue(type_, sync_entity->mutable_specifics());
|
|
|
| // We're always responsible for the parent ID.
|
| sync_entity->set_parent_id_string(data_type_state_.type_root_id);
|
| }
|
|
|
| +void ModelTypeSyncWorkerImpl::TryDecryptPendingUpdates() {
|
| + UpdateResponseDataList response_datas;
|
| +
|
| + ScopedCryptographerRef scoped_cryptographer_ref;
|
| + cryptographer_provider_->InitScopedCryptographerRef(
|
| + &scoped_cryptographer_ref);
|
| + Cryptographer* cryptographer = scoped_cryptographer_ref.Get();
|
| + DCHECK(cryptographer);
|
| +
|
| + for (EntityMap::const_iterator it = entities_.begin(); it != entities_.end();
|
| + ++it) {
|
| + if (it->second->HasPendingUpdate()) {
|
| + const UpdateResponseData& saved_pending = it->second->GetPendingUpdate();
|
| +
|
| + // We assume all pending updates are encrypted items for which we
|
| + // don't have the key.
|
| + DCHECK(saved_pending.specifics.has_encrypted());
|
| +
|
| + if (cryptographer->CanDecrypt(saved_pending.specifics.encrypted())) {
|
| + UpdateResponseData decrypted_response = saved_pending;
|
| + if (DecryptSpecifics(cryptographer,
|
| + saved_pending.specifics,
|
| + &decrypted_response.specifics)) {
|
| + decrypted_response.encryption_key_name =
|
| + saved_pending.specifics.encrypted().key_name();
|
| + response_datas.push_back(decrypted_response);
|
| +
|
| + it->second->ClearPendingUpdate();
|
| + }
|
| + }
|
| + }
|
| + }
|
| +
|
| + if (!response_datas.empty()) {
|
| + type_sync_proxy_->OnUpdateReceived(
|
| + data_type_state_, response_datas, UpdateResponseDataList());
|
| + }
|
| +}
|
| +
|
| +bool ModelTypeSyncWorkerImpl::DecryptSpecifics(
|
| + Cryptographer* cryptographer,
|
| + const sync_pb::EntitySpecifics& in,
|
| + sync_pb::EntitySpecifics* out) {
|
| + DCHECK(in.has_encrypted());
|
| + DCHECK(cryptographer->CanDecrypt(in.encrypted()));
|
| +
|
| + std::string plaintext;
|
| + plaintext = cryptographer->DecryptToString(in.encrypted());
|
| + if (plaintext.empty()) {
|
| + LOG(ERROR) << "Failed to decrypt a decryptable entity";
|
| + return false;
|
| + }
|
| + if (!out->ParseFromString(plaintext)) {
|
| + LOG(ERROR) << "Failed to parse decrypted entity";
|
| + return false;
|
| + }
|
| + return true;
|
| +}
|
| +
|
| } // namespace syncer
|
|
|