Home | History | Annotate | Download | only in engine
      1 // Copyright 2013 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/download.h"
      6 
      7 #include <string>
      8 
      9 #include "base/command_line.h"
     10 #include "sync/engine/process_updates_util.h"
     11 #include "sync/engine/sync_directory_update_handler.h"
     12 #include "sync/engine/syncer.h"
     13 #include "sync/engine/syncer_proto_util.h"
     14 #include "sync/sessions/nudge_tracker.h"
     15 #include "sync/syncable/directory.h"
     16 #include "sync/syncable/nigori_handler.h"
     17 #include "sync/syncable/syncable_read_transaction.h"
     18 
     19 namespace syncer {
     20 
     21 using sessions::StatusController;
     22 using sessions::SyncSession;
     23 using sessions::SyncSessionContext;
     24 using std::string;
     25 
     26 namespace download {
     27 
     28 namespace {
     29 
     30 typedef std::map<ModelType, size_t> TypeToIndexMap;
     31 
     32 SyncerError HandleGetEncryptionKeyResponse(
     33     const sync_pb::ClientToServerResponse& update_response,
     34     syncable::Directory* dir) {
     35   bool success = false;
     36   if (update_response.get_updates().encryption_keys_size() == 0) {
     37     LOG(ERROR) << "Failed to receive encryption key from server.";
     38     return SERVER_RESPONSE_VALIDATION_FAILED;
     39   }
     40   syncable::ReadTransaction trans(FROM_HERE, dir);
     41   syncable::NigoriHandler* nigori_handler = dir->GetNigoriHandler();
     42   success = nigori_handler->SetKeystoreKeys(
     43       update_response.get_updates().encryption_keys(),
     44       &trans);
     45 
     46   DVLOG(1) << "GetUpdates returned "
     47            << update_response.get_updates().encryption_keys_size()
     48            << "encryption keys. Nigori keystore key "
     49            << (success ? "" : "not ") << "updated.";
     50   return (success ? SYNCER_OK : SERVER_RESPONSE_VALIDATION_FAILED);
     51 }
     52 
     53 sync_pb::SyncEnums::GetUpdatesOrigin ConvertConfigureSourceToOrigin(
     54     sync_pb::GetUpdatesCallerInfo::GetUpdatesSource source) {
     55   switch (source) {
     56     // Configurations:
     57     case sync_pb::GetUpdatesCallerInfo::NEWLY_SUPPORTED_DATATYPE:
     58       return sync_pb::SyncEnums::NEWLY_SUPPORTED_DATATYPE;
     59     case sync_pb::GetUpdatesCallerInfo::MIGRATION:
     60       return sync_pb::SyncEnums::MIGRATION;
     61     case sync_pb::GetUpdatesCallerInfo::RECONFIGURATION:
     62       return sync_pb::SyncEnums::RECONFIGURATION;
     63     case sync_pb::GetUpdatesCallerInfo::NEW_CLIENT:
     64       return sync_pb::SyncEnums::NEW_CLIENT;
     65     default:
     66       NOTREACHED();
     67       return sync_pb::SyncEnums::UNKNOWN_ORIGIN;
     68   }
     69 }
     70 
     71 bool ShouldRequestEncryptionKey(
     72     SyncSessionContext* context) {
     73   bool need_encryption_key = false;
     74   if (context->keystore_encryption_enabled()) {
     75     syncable::Directory* dir = context->directory();
     76     syncable::ReadTransaction trans(FROM_HERE, dir);
     77     syncable::NigoriHandler* nigori_handler = dir->GetNigoriHandler();
     78     need_encryption_key = nigori_handler->NeedKeystoreKey(&trans);
     79   }
     80   return need_encryption_key;
     81 }
     82 
     83 void InitDownloadUpdatesContext(
     84     SyncSession* session,
     85     bool create_mobile_bookmarks_folder,
     86     sync_pb::ClientToServerMessage* message) {
     87   message->set_share(session->context()->account_name());
     88   message->set_message_contents(sync_pb::ClientToServerMessage::GET_UPDATES);
     89 
     90   sync_pb::GetUpdatesMessage* get_updates = message->mutable_get_updates();
     91 
     92   // We want folders for our associated types, always.  If we were to set
     93   // this to false, the server would send just the non-container items
     94   // (e.g. Bookmark URLs but not their containing folders).
     95   get_updates->set_fetch_folders(true);
     96 
     97   get_updates->set_create_mobile_bookmarks_folder(
     98       create_mobile_bookmarks_folder);
     99   bool need_encryption_key = ShouldRequestEncryptionKey(session->context());
    100   get_updates->set_need_encryption_key(need_encryption_key);
    101 
    102   // Set legacy GetUpdatesMessage.GetUpdatesCallerInfo information.
    103   get_updates->mutable_caller_info()->set_notifications_enabled(
    104       session->context()->notifications_enabled());
    105 }
    106 
    107 void InitDownloadUpdatesProgress(
    108     ModelTypeSet proto_request_types,
    109     UpdateHandlerMap* handler_map,
    110     sync_pb::GetUpdatesMessage* get_updates) {
    111   for (ModelTypeSet::Iterator it = proto_request_types.First();
    112        it.Good(); it.Inc()) {
    113     UpdateHandlerMap::iterator handler_it = handler_map->find(it.Get());
    114     DCHECK(handler_it != handler_map->end());
    115     sync_pb::DataTypeProgressMarker* progress_marker =
    116         get_updates->add_from_progress_marker();
    117     handler_it->second->GetDownloadProgress(progress_marker);
    118   }
    119 }
    120 
    121 // Builds a map of ModelTypes to indices to progress markers in the given
    122 // |gu_response| message.  The map is returned in the |index_map| parameter.
    123 void PartitionProgressMarkersByType(
    124     const sync_pb::GetUpdatesResponse& gu_response,
    125     ModelTypeSet request_types,
    126     TypeToIndexMap* index_map) {
    127   for (int i = 0; i < gu_response.new_progress_marker_size(); ++i) {
    128     int field_number = gu_response.new_progress_marker(i).data_type_id();
    129     ModelType model_type = GetModelTypeFromSpecificsFieldNumber(field_number);
    130     if (!IsRealDataType(model_type)) {
    131       DLOG(WARNING) << "Unknown field number " << field_number;
    132       continue;
    133     }
    134     if (!request_types.Has(model_type)) {
    135       DLOG(WARNING)
    136           << "Skipping unexpected progress marker for non-enabled type "
    137           << ModelTypeToString(model_type);
    138       continue;
    139     }
    140     index_map->insert(std::make_pair(model_type, i));
    141   }
    142 }
    143 
    144 // Examines the contents of the GetUpdates response message and forwards
    145 // relevant data to the UpdateHandlers for processing and persisting.
    146 bool ProcessUpdateResponseContents(
    147     const sync_pb::GetUpdatesResponse& gu_response,
    148     ModelTypeSet proto_request_types,
    149     UpdateHandlerMap* handler_map,
    150     StatusController* status) {
    151   TypeSyncEntityMap updates_by_type;
    152   PartitionUpdatesByType(gu_response, proto_request_types, &updates_by_type);
    153   DCHECK_EQ(proto_request_types.Size(), updates_by_type.size());
    154 
    155   TypeToIndexMap progress_index_by_type;
    156   PartitionProgressMarkersByType(gu_response,
    157                                  proto_request_types,
    158                                  &progress_index_by_type);
    159   if (proto_request_types.Size() != progress_index_by_type.size()) {
    160     NOTREACHED() << "Missing progress markers in GetUpdates response.";
    161     return false;
    162   }
    163 
    164   // Iterate over these maps in parallel, processing updates for each type.
    165   TypeToIndexMap::iterator progress_marker_iter =
    166       progress_index_by_type.begin();
    167   TypeSyncEntityMap::iterator updates_iter = updates_by_type.begin();
    168   for ( ; (progress_marker_iter != progress_index_by_type.end()
    169            && updates_iter != updates_by_type.end());
    170        ++progress_marker_iter, ++updates_iter) {
    171     DCHECK_EQ(progress_marker_iter->first, updates_iter->first);
    172     ModelType type = progress_marker_iter->first;
    173 
    174     UpdateHandlerMap::iterator update_handler_iter = handler_map->find(type);
    175 
    176     if (update_handler_iter != handler_map->end()) {
    177       update_handler_iter->second->ProcessGetUpdatesResponse(
    178           gu_response.new_progress_marker(progress_marker_iter->second),
    179           updates_iter->second,
    180           status);
    181     } else {
    182       DLOG(WARNING)
    183           << "Ignoring received updates of a type we can't handle.  "
    184           << "Type is: " << ModelTypeToString(type);
    185       continue;
    186     }
    187   }
    188   DCHECK(progress_marker_iter == progress_index_by_type.end()
    189          && updates_iter == updates_by_type.end());
    190 
    191   return true;
    192 }
    193 
    194 }  // namespace
    195 
    196 void BuildNormalDownloadUpdates(
    197     SyncSession* session,
    198     bool create_mobile_bookmarks_folder,
    199     ModelTypeSet request_types,
    200     const sessions::NudgeTracker& nudge_tracker,
    201     sync_pb::ClientToServerMessage* client_to_server_message) {
    202   // Request updates for all requested types.
    203   DVLOG(1) << "Getting updates for types "
    204            << ModelTypeSetToString(request_types);
    205   DCHECK(!request_types.Empty());
    206 
    207   InitDownloadUpdatesContext(
    208       session,
    209       create_mobile_bookmarks_folder,
    210       client_to_server_message);
    211 
    212   BuildNormalDownloadUpdatesImpl(
    213       Intersection(request_types, ProtocolTypes()),
    214       session->context()->update_handler_map(),
    215       nudge_tracker,
    216       client_to_server_message->mutable_get_updates());
    217 }
    218 
    219 void BuildNormalDownloadUpdatesImpl(
    220     ModelTypeSet proto_request_types,
    221     UpdateHandlerMap* update_handler_map,
    222     const sessions::NudgeTracker& nudge_tracker,
    223     sync_pb::GetUpdatesMessage* get_updates) {
    224   DCHECK(!proto_request_types.Empty());
    225 
    226   InitDownloadUpdatesProgress(
    227       proto_request_types,
    228       update_handler_map,
    229       get_updates);
    230 
    231   // Set legacy GetUpdatesMessage.GetUpdatesCallerInfo information.
    232   get_updates->mutable_caller_info()->set_source(
    233       nudge_tracker.updates_source());
    234 
    235   // Set the new and improved version of source, too.
    236   get_updates->set_get_updates_origin(sync_pb::SyncEnums::GU_TRIGGER);
    237 
    238   // Fill in the notification hints.
    239   for (int i = 0; i < get_updates->from_progress_marker_size(); ++i) {
    240     sync_pb::DataTypeProgressMarker* progress_marker =
    241         get_updates->mutable_from_progress_marker(i);
    242     ModelType type = GetModelTypeFromSpecificsFieldNumber(
    243         progress_marker->data_type_id());
    244 
    245     DCHECK(!nudge_tracker.IsTypeThrottled(type))
    246         << "Throttled types should have been removed from the request_types.";
    247 
    248     nudge_tracker.SetLegacyNotificationHint(type, progress_marker);
    249     nudge_tracker.FillProtoMessage(
    250         type,
    251         progress_marker->mutable_get_update_triggers());
    252   }
    253 }
    254 
    255 void BuildDownloadUpdatesForConfigure(
    256     SyncSession* session,
    257     bool create_mobile_bookmarks_folder,
    258     sync_pb::GetUpdatesCallerInfo::GetUpdatesSource source,
    259     ModelTypeSet request_types,
    260     sync_pb::ClientToServerMessage* client_to_server_message) {
    261   // Request updates for all enabled types.
    262   DVLOG(1) << "Initial download for types "
    263            << ModelTypeSetToString(request_types);
    264 
    265   InitDownloadUpdatesContext(
    266       session,
    267       create_mobile_bookmarks_folder,
    268       client_to_server_message);
    269   BuildDownloadUpdatesForConfigureImpl(
    270       Intersection(request_types, ProtocolTypes()),
    271       session->context()->update_handler_map(),
    272       source,
    273       client_to_server_message->mutable_get_updates());
    274 }
    275 
    276 void BuildDownloadUpdatesForConfigureImpl(
    277     ModelTypeSet proto_request_types,
    278     UpdateHandlerMap* update_handler_map,
    279     sync_pb::GetUpdatesCallerInfo::GetUpdatesSource source,
    280     sync_pb::GetUpdatesMessage* get_updates) {
    281   DCHECK(!proto_request_types.Empty());
    282 
    283   InitDownloadUpdatesProgress(
    284       proto_request_types,
    285       update_handler_map,
    286       get_updates);
    287 
    288   // Set legacy GetUpdatesMessage.GetUpdatesCallerInfo information.
    289   get_updates->mutable_caller_info()->set_source(source);
    290 
    291   // Set the new and improved version of source, too.
    292   sync_pb::SyncEnums::GetUpdatesOrigin origin =
    293       ConvertConfigureSourceToOrigin(source);
    294   get_updates->set_get_updates_origin(origin);
    295 }
    296 
    297 void BuildDownloadUpdatesForPoll(
    298     SyncSession* session,
    299     bool create_mobile_bookmarks_folder,
    300     ModelTypeSet request_types,
    301     sync_pb::ClientToServerMessage* client_to_server_message) {
    302   DVLOG(1) << "Polling for types "
    303            << ModelTypeSetToString(request_types);
    304 
    305   InitDownloadUpdatesContext(
    306       session,
    307       create_mobile_bookmarks_folder,
    308       client_to_server_message);
    309   BuildDownloadUpdatesForPollImpl(
    310       Intersection(request_types, ProtocolTypes()),
    311       session->context()->update_handler_map(),
    312       client_to_server_message->mutable_get_updates());
    313 }
    314 
    315 void BuildDownloadUpdatesForPollImpl(
    316     ModelTypeSet proto_request_types,
    317     UpdateHandlerMap* update_handler_map,
    318     sync_pb::GetUpdatesMessage* get_updates) {
    319   DCHECK(!proto_request_types.Empty());
    320 
    321   InitDownloadUpdatesProgress(
    322       proto_request_types,
    323       update_handler_map,
    324       get_updates);
    325 
    326   // Set legacy GetUpdatesMessage.GetUpdatesCallerInfo information.
    327   get_updates->mutable_caller_info()->set_source(
    328       sync_pb::GetUpdatesCallerInfo::PERIODIC);
    329 
    330   // Set the new and improved version of source, too.
    331   get_updates->set_get_updates_origin(sync_pb::SyncEnums::PERIODIC);
    332 }
    333 
    334 SyncerError ExecuteDownloadUpdates(
    335     ModelTypeSet request_types,
    336     SyncSession* session,
    337     sync_pb::ClientToServerMessage* msg) {
    338   sync_pb::ClientToServerResponse update_response;
    339   StatusController* status = session->mutable_status_controller();
    340   bool need_encryption_key = ShouldRequestEncryptionKey(session->context());
    341 
    342   if (session->context()->debug_info_getter()) {
    343     sync_pb::DebugInfo* debug_info = msg->mutable_debug_info();
    344     CopyClientDebugInfo(session->context()->debug_info_getter(), debug_info);
    345   }
    346 
    347   SyncerError result = SyncerProtoUtil::PostClientToServerMessage(
    348       msg,
    349       &update_response,
    350       session);
    351 
    352   DVLOG(2) << SyncerProtoUtil::ClientToServerResponseDebugString(
    353       update_response);
    354 
    355   if (result != SYNCER_OK) {
    356     LOG(ERROR) << "PostClientToServerMessage() failed during GetUpdates";
    357     return result;
    358   }
    359 
    360   DVLOG(1) << "GetUpdates "
    361            << " returned " << update_response.get_updates().entries_size()
    362            << " updates and indicated "
    363            << update_response.get_updates().changes_remaining()
    364            << " updates left on server.";
    365 
    366   if (session->context()->debug_info_getter()) {
    367     // Clear debug info now that we have successfully sent it to the server.
    368     DVLOG(1) << "Clearing client debug info.";
    369     session->context()->debug_info_getter()->ClearDebugInfo();
    370   }
    371 
    372   if (need_encryption_key ||
    373       update_response.get_updates().encryption_keys_size() > 0) {
    374     syncable::Directory* dir = session->context()->directory();
    375     status->set_last_get_key_result(
    376         HandleGetEncryptionKeyResponse(update_response, dir));
    377   }
    378 
    379   const ModelTypeSet proto_request_types =
    380       Intersection(request_types, ProtocolTypes());
    381 
    382   return ProcessResponse(update_response.get_updates(),
    383                          proto_request_types,
    384                          session->context()->update_handler_map(),
    385                          status);
    386 }
    387 
    388 SyncerError ProcessResponse(
    389     const sync_pb::GetUpdatesResponse& gu_response,
    390     ModelTypeSet proto_request_types,
    391     UpdateHandlerMap* handler_map,
    392     StatusController* status) {
    393   status->increment_num_updates_downloaded_by(gu_response.entries_size());
    394 
    395   // The changes remaining field is used to prevent the client from looping.  If
    396   // that field is being set incorrectly, we're in big trouble.
    397   if (!gu_response.has_changes_remaining()) {
    398     return SERVER_RESPONSE_VALIDATION_FAILED;
    399   }
    400   status->set_num_server_changes_remaining(gu_response.changes_remaining());
    401 
    402 
    403   if (!ProcessUpdateResponseContents(gu_response,
    404                                      proto_request_types,
    405                                      handler_map,
    406                                      status)) {
    407     return SERVER_RESPONSE_VALIDATION_FAILED;
    408   }
    409 
    410   if (gu_response.changes_remaining() == 0) {
    411     return SYNCER_OK;
    412   } else {
    413     return SERVER_MORE_TO_DOWNLOAD;
    414   }
    415 }
    416 
    417 void CopyClientDebugInfo(
    418     sessions::DebugInfoGetter* debug_info_getter,
    419     sync_pb::DebugInfo* debug_info) {
    420   DVLOG(1) << "Copying client debug info to send.";
    421   debug_info_getter->GetDebugInfo(debug_info);
    422 }
    423 
    424 }  // namespace download
    425 
    426 }  // namespace syncer
    427