1// Copyright 2014 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 "sync/engine/get_updates_processor.h" 6 7#include <map> 8 9#include "base/debug/trace_event.h" 10#include "sync/engine/get_updates_delegate.h" 11#include "sync/engine/syncer_proto_util.h" 12#include "sync/engine/update_handler.h" 13#include "sync/internal_api/public/events/get_updates_response_event.h" 14#include "sync/protocol/sync.pb.h" 15#include "sync/sessions/status_controller.h" 16#include "sync/sessions/sync_session.h" 17#include "sync/syncable/directory.h" 18#include "sync/syncable/nigori_handler.h" 19#include "sync/syncable/syncable_read_transaction.h" 20 21typedef std::vector<const sync_pb::SyncEntity*> SyncEntityList; 22typedef std::map<syncer::ModelType, SyncEntityList> TypeSyncEntityMap; 23 24namespace syncer { 25 26typedef std::map<ModelType, size_t> TypeToIndexMap; 27 28namespace { 29 30bool ShouldRequestEncryptionKey(sessions::SyncSessionContext* context) { 31 syncable::Directory* dir = context->directory(); 32 syncable::ReadTransaction trans(FROM_HERE, dir); 33 syncable::NigoriHandler* nigori_handler = dir->GetNigoriHandler(); 34 return nigori_handler->NeedKeystoreKey(&trans); 35} 36 37 38SyncerError HandleGetEncryptionKeyResponse( 39 const sync_pb::ClientToServerResponse& update_response, 40 syncable::Directory* dir) { 41 bool success = false; 42 if (update_response.get_updates().encryption_keys_size() == 0) { 43 LOG(ERROR) << "Failed to receive encryption key from server."; 44 return SERVER_RESPONSE_VALIDATION_FAILED; 45 } 46 syncable::ReadTransaction trans(FROM_HERE, dir); 47 syncable::NigoriHandler* nigori_handler = dir->GetNigoriHandler(); 48 success = nigori_handler->SetKeystoreKeys( 49 update_response.get_updates().encryption_keys(), 50 &trans); 51 52 DVLOG(1) << "GetUpdates returned " 53 << update_response.get_updates().encryption_keys_size() 54 << "encryption keys. Nigori keystore key " 55 << (success ? "" : "not ") << "updated."; 56 return (success ? SYNCER_OK : SERVER_RESPONSE_VALIDATION_FAILED); 57} 58 59// Given a GetUpdates response, iterates over all the returned items and 60// divides them according to their type. Outputs a map from model types to 61// received SyncEntities. The output map will have entries (possibly empty) 62// for all types in |requested_types|. 63void PartitionUpdatesByType(const sync_pb::GetUpdatesResponse& gu_response, 64 ModelTypeSet requested_types, 65 TypeSyncEntityMap* updates_by_type) { 66 int update_count = gu_response.entries().size(); 67 for (ModelTypeSet::Iterator it = requested_types.First(); 68 it.Good(); it.Inc()) { 69 updates_by_type->insert(std::make_pair(it.Get(), SyncEntityList())); 70 } 71 for (int i = 0; i < update_count; ++i) { 72 const sync_pb::SyncEntity& update = gu_response.entries(i); 73 ModelType type = GetModelType(update); 74 if (!IsRealDataType(type)) { 75 NOTREACHED() << "Received update with invalid type."; 76 continue; 77 } 78 79 TypeSyncEntityMap::iterator it = updates_by_type->find(type); 80 if (it == updates_by_type->end()) { 81 NOTREACHED() << "Received update for unexpected type " 82 << ModelTypeToString(type); 83 continue; 84 } 85 86 it->second.push_back(&update); 87 } 88} 89 90// Builds a map of ModelTypes to indices to progress markers in the given 91// |gu_response| message. The map is returned in the |index_map| parameter. 92void PartitionProgressMarkersByType( 93 const sync_pb::GetUpdatesResponse& gu_response, 94 ModelTypeSet request_types, 95 TypeToIndexMap* index_map) { 96 for (int i = 0; i < gu_response.new_progress_marker_size(); ++i) { 97 int field_number = gu_response.new_progress_marker(i).data_type_id(); 98 ModelType model_type = GetModelTypeFromSpecificsFieldNumber(field_number); 99 if (!IsRealDataType(model_type)) { 100 DLOG(WARNING) << "Unknown field number " << field_number; 101 continue; 102 } 103 if (!request_types.Has(model_type)) { 104 DLOG(WARNING) 105 << "Skipping unexpected progress marker for non-enabled type " 106 << ModelTypeToString(model_type); 107 continue; 108 } 109 index_map->insert(std::make_pair(model_type, i)); 110 } 111} 112 113void PartitionContextMutationsByType( 114 const sync_pb::GetUpdatesResponse& gu_response, 115 ModelTypeSet request_types, 116 TypeToIndexMap* index_map) { 117 for (int i = 0; i < gu_response.context_mutations_size(); ++i) { 118 int field_number = gu_response.context_mutations(i).data_type_id(); 119 ModelType model_type = GetModelTypeFromSpecificsFieldNumber(field_number); 120 if (!IsRealDataType(model_type)) { 121 DLOG(WARNING) << "Unknown field number " << field_number; 122 continue; 123 } 124 if (!request_types.Has(model_type)) { 125 DLOG(WARNING) 126 << "Skipping unexpected context mutation for non-enabled type " 127 << ModelTypeToString(model_type); 128 continue; 129 } 130 index_map->insert(std::make_pair(model_type, i)); 131 } 132} 133 134// Initializes the parts of the GetUpdatesMessage that depend on shared state, 135// like the ShouldRequestEncryptionKey() status. This is kept separate from the 136// other of the message-building functions to make the rest of the code easier 137// to test. 138void InitDownloadUpdatesContext( 139 sessions::SyncSession* session, 140 bool create_mobile_bookmarks_folder, 141 sync_pb::ClientToServerMessage* message) { 142 message->set_share(session->context()->account_name()); 143 message->set_message_contents(sync_pb::ClientToServerMessage::GET_UPDATES); 144 145 sync_pb::GetUpdatesMessage* get_updates = message->mutable_get_updates(); 146 147 // We want folders for our associated types, always. If we were to set 148 // this to false, the server would send just the non-container items 149 // (e.g. Bookmark URLs but not their containing folders). 150 get_updates->set_fetch_folders(true); 151 152 get_updates->set_create_mobile_bookmarks_folder( 153 create_mobile_bookmarks_folder); 154 bool need_encryption_key = ShouldRequestEncryptionKey(session->context()); 155 get_updates->set_need_encryption_key(need_encryption_key); 156 157 // Set legacy GetUpdatesMessage.GetUpdatesCallerInfo information. 158 get_updates->mutable_caller_info()->set_notifications_enabled( 159 session->context()->notifications_enabled()); 160} 161 162} // namespace 163 164GetUpdatesProcessor::GetUpdatesProcessor(UpdateHandlerMap* update_handler_map, 165 const GetUpdatesDelegate& delegate) 166 : update_handler_map_(update_handler_map), delegate_(delegate) {} 167 168GetUpdatesProcessor::~GetUpdatesProcessor() {} 169 170SyncerError GetUpdatesProcessor::DownloadUpdates( 171 ModelTypeSet request_types, 172 sessions::SyncSession* session, 173 bool create_mobile_bookmarks_folder) { 174 TRACE_EVENT0("sync", "DownloadUpdates"); 175 176 sync_pb::ClientToServerMessage message; 177 InitDownloadUpdatesContext(session, 178 create_mobile_bookmarks_folder, 179 &message); 180 PrepareGetUpdates(request_types, &message); 181 182 SyncerError result = ExecuteDownloadUpdates(request_types, session, &message); 183 session->mutable_status_controller()->set_last_download_updates_result( 184 result); 185 return result; 186} 187 188void GetUpdatesProcessor::PrepareGetUpdates( 189 ModelTypeSet gu_types, 190 sync_pb::ClientToServerMessage* message) { 191 sync_pb::GetUpdatesMessage* get_updates = message->mutable_get_updates(); 192 193 for (ModelTypeSet::Iterator it = gu_types.First(); it.Good(); it.Inc()) { 194 UpdateHandlerMap::iterator handler_it = update_handler_map_->find(it.Get()); 195 DCHECK(handler_it != update_handler_map_->end()) 196 << "Failed to look up handler for " << ModelTypeToString(it.Get()); 197 sync_pb::DataTypeProgressMarker* progress_marker = 198 get_updates->add_from_progress_marker(); 199 handler_it->second->GetDownloadProgress(progress_marker); 200 progress_marker->clear_gc_directive(); 201 202 sync_pb::DataTypeContext context; 203 handler_it->second->GetDataTypeContext(&context); 204 if (!context.context().empty()) 205 get_updates->add_client_contexts()->Swap(&context); 206 } 207 208 delegate_.HelpPopulateGuMessage(get_updates); 209} 210 211SyncerError GetUpdatesProcessor::ExecuteDownloadUpdates( 212 ModelTypeSet request_types, 213 sessions::SyncSession* session, 214 sync_pb::ClientToServerMessage* msg) { 215 sync_pb::ClientToServerResponse update_response; 216 sessions::StatusController* status = session->mutable_status_controller(); 217 bool need_encryption_key = ShouldRequestEncryptionKey(session->context()); 218 219 if (session->context()->debug_info_getter()) { 220 sync_pb::DebugInfo* debug_info = msg->mutable_debug_info(); 221 CopyClientDebugInfo(session->context()->debug_info_getter(), debug_info); 222 } 223 224 session->SendProtocolEvent( 225 *(delegate_.GetNetworkRequestEvent(base::Time::Now(), *msg))); 226 227 SyncerError result = SyncerProtoUtil::PostClientToServerMessage( 228 msg, 229 &update_response, 230 session); 231 232 DVLOG(2) << SyncerProtoUtil::ClientToServerResponseDebugString( 233 update_response); 234 235 if (result != SYNCER_OK) { 236 GetUpdatesResponseEvent response_event( 237 base::Time::Now(), update_response, result); 238 session->SendProtocolEvent(response_event); 239 240 LOG(ERROR) << "PostClientToServerMessage() failed during GetUpdates"; 241 return result; 242 } 243 244 DVLOG(1) << "GetUpdates returned " 245 << update_response.get_updates().entries_size() 246 << " updates."; 247 248 249 if (session->context()->debug_info_getter()) { 250 // Clear debug info now that we have successfully sent it to the server. 251 DVLOG(1) << "Clearing client debug info."; 252 session->context()->debug_info_getter()->ClearDebugInfo(); 253 } 254 255 if (need_encryption_key || 256 update_response.get_updates().encryption_keys_size() > 0) { 257 syncable::Directory* dir = session->context()->directory(); 258 status->set_last_get_key_result( 259 HandleGetEncryptionKeyResponse(update_response, dir)); 260 } 261 262 SyncerError process_result = ProcessResponse(update_response.get_updates(), 263 request_types, 264 status); 265 266 GetUpdatesResponseEvent response_event( 267 base::Time::Now(), update_response, process_result); 268 session->SendProtocolEvent(response_event); 269 270 DVLOG(1) << "GetUpdates result: " << process_result; 271 272 return process_result; 273} 274 275SyncerError GetUpdatesProcessor::ProcessResponse( 276 const sync_pb::GetUpdatesResponse& gu_response, 277 ModelTypeSet request_types, 278 sessions::StatusController* status) { 279 status->increment_num_updates_downloaded_by(gu_response.entries_size()); 280 281 // The changes remaining field is used to prevent the client from looping. If 282 // that field is being set incorrectly, we're in big trouble. 283 if (!gu_response.has_changes_remaining()) { 284 return SERVER_RESPONSE_VALIDATION_FAILED; 285 } 286 287 syncer::SyncerError result = 288 ProcessGetUpdatesResponse(request_types, gu_response, status); 289 if (result != syncer::SYNCER_OK) 290 return result; 291 292 if (gu_response.changes_remaining() == 0) { 293 return SYNCER_OK; 294 } else { 295 return SERVER_MORE_TO_DOWNLOAD; 296 } 297} 298 299syncer::SyncerError GetUpdatesProcessor::ProcessGetUpdatesResponse( 300 ModelTypeSet gu_types, 301 const sync_pb::GetUpdatesResponse& gu_response, 302 sessions::StatusController* status_controller) { 303 TypeSyncEntityMap updates_by_type; 304 PartitionUpdatesByType(gu_response, gu_types, &updates_by_type); 305 DCHECK_EQ(gu_types.Size(), updates_by_type.size()); 306 307 TypeToIndexMap progress_index_by_type; 308 PartitionProgressMarkersByType(gu_response, 309 gu_types, 310 &progress_index_by_type); 311 if (gu_types.Size() != progress_index_by_type.size()) { 312 NOTREACHED() << "Missing progress markers in GetUpdates response."; 313 return syncer::SERVER_RESPONSE_VALIDATION_FAILED; 314 } 315 316 TypeToIndexMap context_by_type; 317 PartitionContextMutationsByType(gu_response, gu_types, &context_by_type); 318 319 // Iterate over these maps in parallel, processing updates for each type. 320 TypeToIndexMap::iterator progress_marker_iter = 321 progress_index_by_type.begin(); 322 TypeSyncEntityMap::iterator updates_iter = updates_by_type.begin(); 323 for (; (progress_marker_iter != progress_index_by_type.end() 324 && updates_iter != updates_by_type.end()); 325 ++progress_marker_iter, ++updates_iter) { 326 DCHECK_EQ(progress_marker_iter->first, updates_iter->first); 327 ModelType type = progress_marker_iter->first; 328 329 UpdateHandlerMap::iterator update_handler_iter = 330 update_handler_map_->find(type); 331 332 sync_pb::DataTypeContext context; 333 TypeToIndexMap::iterator context_iter = context_by_type.find(type); 334 if (context_iter != context_by_type.end()) 335 context.CopyFrom(gu_response.context_mutations(context_iter->second)); 336 337 if (update_handler_iter != update_handler_map_->end()) { 338 syncer::SyncerError result = 339 update_handler_iter->second->ProcessGetUpdatesResponse( 340 gu_response.new_progress_marker(progress_marker_iter->second), 341 context, 342 updates_iter->second, 343 status_controller); 344 if (result != syncer::SYNCER_OK) 345 return result; 346 } else { 347 DLOG(WARNING) 348 << "Ignoring received updates of a type we can't handle. " 349 << "Type is: " << ModelTypeToString(type); 350 continue; 351 } 352 } 353 DCHECK(progress_marker_iter == progress_index_by_type.end() && 354 updates_iter == updates_by_type.end()); 355 356 return syncer::SYNCER_OK; 357} 358 359void GetUpdatesProcessor::ApplyUpdates( 360 ModelTypeSet gu_types, 361 sessions::StatusController* status_controller) { 362 delegate_.ApplyUpdates(gu_types, status_controller, update_handler_map_); 363} 364 365void GetUpdatesProcessor::CopyClientDebugInfo( 366 sessions::DebugInfoGetter* debug_info_getter, 367 sync_pb::DebugInfo* debug_info) { 368 DVLOG(1) << "Copying client debug info to send."; 369 debug_info_getter->GetDebugInfo(debug_info); 370} 371 372} // namespace syncer 373