Home | History | Annotate | Download | only in engine
      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 "sync/engine/syncer_proto_util.h"
      6 
      7 #include "base/format_macros.h"
      8 #include "base/strings/stringprintf.h"
      9 #include "google_apis/google_api_keys.h"
     10 #include "sync/engine/net/server_connection_manager.h"
     11 #include "sync/engine/syncer.h"
     12 #include "sync/engine/syncer_types.h"
     13 #include "sync/engine/traffic_logger.h"
     14 #include "sync/internal_api/public/base/model_type.h"
     15 #include "sync/protocol/sync_enums.pb.h"
     16 #include "sync/protocol/sync_protocol_error.h"
     17 #include "sync/sessions/sync_session.h"
     18 #include "sync/syncable/directory.h"
     19 #include "sync/syncable/entry.h"
     20 #include "sync/syncable/syncable-inl.h"
     21 #include "sync/syncable/syncable_proto_util.h"
     22 #include "sync/util/time.h"
     23 
     24 using std::string;
     25 using std::stringstream;
     26 using sync_pb::ClientToServerMessage;
     27 using sync_pb::ClientToServerResponse;
     28 
     29 namespace syncer {
     30 
     31 using sessions::SyncSession;
     32 using syncable::BASE_VERSION;
     33 using syncable::CTIME;
     34 using syncable::ID;
     35 using syncable::IS_DEL;
     36 using syncable::IS_DIR;
     37 using syncable::IS_UNSYNCED;
     38 using syncable::MTIME;
     39 using syncable::PARENT_ID;
     40 
     41 namespace {
     42 
     43 // Time to backoff syncing after receiving a throttled response.
     44 const int kSyncDelayAfterThrottled = 2 * 60 * 60;  // 2 hours
     45 
     46 void LogResponseProfilingData(const ClientToServerResponse& response) {
     47   if (response.has_profiling_data()) {
     48     stringstream response_trace;
     49     response_trace << "Server response trace:";
     50 
     51     if (response.profiling_data().has_user_lookup_time()) {
     52       response_trace << " user lookup: "
     53                      << response.profiling_data().user_lookup_time() << "ms";
     54     }
     55 
     56     if (response.profiling_data().has_meta_data_write_time()) {
     57       response_trace << " meta write: "
     58                      << response.profiling_data().meta_data_write_time()
     59                      << "ms";
     60     }
     61 
     62     if (response.profiling_data().has_meta_data_read_time()) {
     63       response_trace << " meta read: "
     64                      << response.profiling_data().meta_data_read_time() << "ms";
     65     }
     66 
     67     if (response.profiling_data().has_file_data_write_time()) {
     68       response_trace << " file write: "
     69                      << response.profiling_data().file_data_write_time()
     70                      << "ms";
     71     }
     72 
     73     if (response.profiling_data().has_file_data_read_time()) {
     74       response_trace << " file read: "
     75                      << response.profiling_data().file_data_read_time() << "ms";
     76     }
     77 
     78     if (response.profiling_data().has_total_request_time()) {
     79       response_trace << " total time: "
     80                      << response.profiling_data().total_request_time() << "ms";
     81     }
     82     DVLOG(1) << response_trace.str();
     83   }
     84 }
     85 
     86 SyncerError ServerConnectionErrorAsSyncerError(
     87     const HttpResponse::ServerConnectionCode server_status) {
     88   switch (server_status) {
     89     case HttpResponse::CONNECTION_UNAVAILABLE:
     90       return NETWORK_CONNECTION_UNAVAILABLE;
     91     case HttpResponse::IO_ERROR:
     92       return NETWORK_IO_ERROR;
     93     case HttpResponse::SYNC_SERVER_ERROR:
     94       // FIXME what does this mean?
     95       return SYNC_SERVER_ERROR;
     96     case HttpResponse::SYNC_AUTH_ERROR:
     97       return SYNC_AUTH_ERROR;
     98     case HttpResponse::RETRY:
     99       return SERVER_RETURN_TRANSIENT_ERROR;
    100     case HttpResponse::SERVER_CONNECTION_OK:
    101     case HttpResponse::NONE:
    102     default:
    103       NOTREACHED();
    104       return UNSET;
    105   }
    106 }
    107 
    108 SyncProtocolErrorType ConvertSyncProtocolErrorTypePBToLocalType(
    109     const sync_pb::SyncEnums::ErrorType& error_type) {
    110   switch (error_type) {
    111     case sync_pb::SyncEnums::SUCCESS:
    112       return SYNC_SUCCESS;
    113     case sync_pb::SyncEnums::NOT_MY_BIRTHDAY:
    114       return NOT_MY_BIRTHDAY;
    115     case sync_pb::SyncEnums::THROTTLED:
    116       return THROTTLED;
    117     case sync_pb::SyncEnums::CLEAR_PENDING:
    118       return CLEAR_PENDING;
    119     case sync_pb::SyncEnums::TRANSIENT_ERROR:
    120       return TRANSIENT_ERROR;
    121     case sync_pb::SyncEnums::MIGRATION_DONE:
    122       return MIGRATION_DONE;
    123     case sync_pb::SyncEnums::DISABLED_BY_ADMIN:
    124       return DISABLED_BY_ADMIN;
    125     case sync_pb::SyncEnums::UNKNOWN:
    126       return UNKNOWN_ERROR;
    127     case sync_pb::SyncEnums::USER_NOT_ACTIVATED:
    128     case sync_pb::SyncEnums::AUTH_INVALID:
    129     case sync_pb::SyncEnums::ACCESS_DENIED:
    130       return INVALID_CREDENTIAL;
    131     default:
    132       NOTREACHED();
    133       return UNKNOWN_ERROR;
    134   }
    135 }
    136 
    137 ClientAction ConvertClientActionPBToLocalClientAction(
    138     const sync_pb::SyncEnums::Action& action) {
    139   switch (action) {
    140     case sync_pb::SyncEnums::UPGRADE_CLIENT:
    141       return UPGRADE_CLIENT;
    142     case sync_pb::SyncEnums::CLEAR_USER_DATA_AND_RESYNC:
    143       return CLEAR_USER_DATA_AND_RESYNC;
    144     case sync_pb::SyncEnums::ENABLE_SYNC_ON_ACCOUNT:
    145       return ENABLE_SYNC_ON_ACCOUNT;
    146     case sync_pb::SyncEnums::STOP_AND_RESTART_SYNC:
    147       return STOP_AND_RESTART_SYNC;
    148     case sync_pb::SyncEnums::DISABLE_SYNC_ON_CLIENT:
    149       return DISABLE_SYNC_ON_CLIENT;
    150     case sync_pb::SyncEnums::UNKNOWN_ACTION:
    151       return UNKNOWN_ACTION;
    152     default:
    153       NOTREACHED();
    154       return UNKNOWN_ACTION;
    155   }
    156 }
    157 
    158 }  // namespace
    159 
    160 ModelTypeSet GetTypesToMigrate(const ClientToServerResponse& response) {
    161   ModelTypeSet to_migrate;
    162   for (int i = 0; i < response.migrated_data_type_id_size(); i++) {
    163     int field_number = response.migrated_data_type_id(i);
    164     ModelType model_type = GetModelTypeFromSpecificsFieldNumber(field_number);
    165     if (!IsRealDataType(model_type)) {
    166       DLOG(WARNING) << "Unknown field number " << field_number;
    167       continue;
    168     }
    169     to_migrate.Put(model_type);
    170   }
    171   return to_migrate;
    172 }
    173 
    174 SyncProtocolError ConvertErrorPBToLocalType(
    175     const sync_pb::ClientToServerResponse_Error& error) {
    176   SyncProtocolError sync_protocol_error;
    177   sync_protocol_error.error_type = ConvertSyncProtocolErrorTypePBToLocalType(
    178       error.error_type());
    179   sync_protocol_error.error_description = error.error_description();
    180   sync_protocol_error.url = error.url();
    181   sync_protocol_error.action = ConvertClientActionPBToLocalClientAction(
    182       error.action());
    183 
    184   if (error.error_data_type_ids_size() > 0) {
    185     // THROTTLED is currently the only error code that uses |error_data_types|.
    186     DCHECK_EQ(error.error_type(), sync_pb::SyncEnums::THROTTLED);
    187     for (int i = 0; i < error.error_data_type_ids_size(); ++i) {
    188       int field_number = error.error_data_type_ids(i);
    189       ModelType model_type =
    190           GetModelTypeFromSpecificsFieldNumber(field_number);
    191       if (!IsRealDataType(model_type)) {
    192         DLOG(WARNING) << "Unknown field number " << field_number;
    193         continue;
    194       }
    195       sync_protocol_error.error_data_types.Put(model_type);
    196     }
    197   }
    198 
    199   return sync_protocol_error;
    200 }
    201 
    202 // static
    203 bool SyncerProtoUtil::VerifyResponseBirthday(
    204     const ClientToServerResponse& response,
    205     syncable::Directory* dir) {
    206 
    207   std::string local_birthday = dir->store_birthday();
    208 
    209   if (local_birthday.empty()) {
    210     if (!response.has_store_birthday()) {
    211       LOG(WARNING) << "Expected a birthday on first sync.";
    212       return false;
    213     }
    214 
    215     DVLOG(1) << "New store birthday: " << response.store_birthday();
    216     dir->set_store_birthday(response.store_birthday());
    217     return true;
    218   }
    219 
    220   // Error situation, but we're not stuck.
    221   if (!response.has_store_birthday()) {
    222     LOG(WARNING) << "No birthday in server response?";
    223     return true;
    224   }
    225 
    226   if (response.store_birthday() != local_birthday) {
    227     LOG(WARNING) << "Birthday changed, showing syncer stuck";
    228     return false;
    229   }
    230 
    231   return true;
    232 }
    233 
    234 // static
    235 bool SyncerProtoUtil::IsSyncDisabledByAdmin(
    236     const sync_pb::ClientToServerResponse& response) {
    237   return (response.has_error_code() &&
    238           response.error_code() == sync_pb::SyncEnums::DISABLED_BY_ADMIN);
    239 }
    240 
    241 // static
    242 void SyncerProtoUtil::AddRequestBirthday(syncable::Directory* dir,
    243                                          ClientToServerMessage* msg) {
    244   if (!dir->store_birthday().empty())
    245     msg->set_store_birthday(dir->store_birthday());
    246 }
    247 
    248 // static
    249 void SyncerProtoUtil::AddBagOfChips(syncable::Directory* dir,
    250                                     ClientToServerMessage* msg) {
    251   msg->mutable_bag_of_chips()->ParseFromString(dir->bag_of_chips());
    252 }
    253 
    254 // static
    255 void SyncerProtoUtil::SetProtocolVersion(ClientToServerMessage* msg) {
    256   const int current_version =
    257       ClientToServerMessage::default_instance().protocol_version();
    258   msg->set_protocol_version(current_version);
    259 }
    260 
    261 // static
    262 bool SyncerProtoUtil::PostAndProcessHeaders(ServerConnectionManager* scm,
    263                                             sessions::SyncSession* session,
    264                                             const ClientToServerMessage& msg,
    265                                             ClientToServerResponse* response) {
    266   ServerConnectionManager::PostBufferParams params;
    267   DCHECK(msg.has_protocol_version());
    268   DCHECK_EQ(msg.protocol_version(),
    269             ClientToServerMessage::default_instance().protocol_version());
    270   msg.SerializeToString(&params.buffer_in);
    271 
    272   ScopedServerStatusWatcher server_status_watcher(scm, &params.response);
    273   // Fills in params.buffer_out and params.response.
    274   if (!scm->PostBufferWithCachedAuth(&params, &server_status_watcher)) {
    275     LOG(WARNING) << "Error posting from syncer:" << params.response;
    276     return false;
    277   }
    278 
    279   std::string new_token = params.response.update_client_auth_header;
    280   if (!new_token.empty()) {
    281     SyncEngineEvent event(SyncEngineEvent::UPDATED_TOKEN);
    282     event.updated_token = new_token;
    283     session->context()->NotifyListeners(event);
    284   }
    285 
    286   if (response->ParseFromString(params.buffer_out)) {
    287     // TODO(tim): This is an egregious layering violation (bug 35060).
    288     switch (response->error_code()) {
    289       case sync_pb::SyncEnums::ACCESS_DENIED:
    290       case sync_pb::SyncEnums::AUTH_INVALID:
    291       case sync_pb::SyncEnums::USER_NOT_ACTIVATED:
    292         // Fires on ScopedServerStatusWatcher
    293         params.response.server_status = HttpResponse::SYNC_AUTH_ERROR;
    294         return false;
    295       default:
    296         return true;
    297     }
    298   }
    299 
    300   return false;
    301 }
    302 
    303 base::TimeDelta SyncerProtoUtil::GetThrottleDelay(
    304     const ClientToServerResponse& response) {
    305   base::TimeDelta throttle_delay =
    306       base::TimeDelta::FromSeconds(kSyncDelayAfterThrottled);
    307   if (response.has_client_command()) {
    308     const sync_pb::ClientCommand& command = response.client_command();
    309     if (command.has_throttle_delay_seconds()) {
    310       throttle_delay =
    311           base::TimeDelta::FromSeconds(command.throttle_delay_seconds());
    312     }
    313   }
    314   return throttle_delay;
    315 }
    316 
    317 namespace {
    318 
    319 // Helper function for an assertion in PostClientToServerMessage.
    320 bool IsVeryFirstGetUpdates(const ClientToServerMessage& message) {
    321   if (!message.has_get_updates())
    322     return false;
    323   DCHECK_LT(0, message.get_updates().from_progress_marker_size());
    324   for (int i = 0; i < message.get_updates().from_progress_marker_size(); ++i) {
    325     if (!message.get_updates().from_progress_marker(i).token().empty())
    326       return false;
    327   }
    328   return true;
    329 }
    330 
    331 // TODO(lipalani) : Rename these function names as per the CR for issue 7740067.
    332 SyncProtocolError ConvertLegacyErrorCodeToNewError(
    333     const sync_pb::SyncEnums::ErrorType& error_type) {
    334   SyncProtocolError error;
    335   error.error_type = ConvertSyncProtocolErrorTypePBToLocalType(error_type);
    336   if (error_type == sync_pb::SyncEnums::CLEAR_PENDING ||
    337       error_type == sync_pb::SyncEnums::NOT_MY_BIRTHDAY) {
    338     error.action = DISABLE_SYNC_ON_CLIENT;
    339   } else if (error_type == sync_pb::SyncEnums::DISABLED_BY_ADMIN) {
    340     error.action = STOP_SYNC_FOR_DISABLED_ACCOUNT;
    341   }  // There is no other action we can compute for legacy server.
    342   return error;
    343 }
    344 
    345 }  // namespace
    346 
    347 // static
    348 SyncerError SyncerProtoUtil::PostClientToServerMessage(
    349     ClientToServerMessage* msg,
    350     ClientToServerResponse* response,
    351     SyncSession* session) {
    352   CHECK(response);
    353   DCHECK(!msg->get_updates().has_from_timestamp());  // Deprecated.
    354   DCHECK(!msg->get_updates().has_requested_types());  // Deprecated.
    355 
    356   // Add must-have fields.
    357   SetProtocolVersion(msg);
    358   AddRequestBirthday(session->context()->directory(), msg);
    359   DCHECK(msg->has_store_birthday() || IsVeryFirstGetUpdates(*msg));
    360   AddBagOfChips(session->context()->directory(), msg);
    361   msg->set_api_key(google_apis::GetAPIKey());
    362   msg->mutable_client_status()->CopyFrom(session->context()->client_status());
    363   msg->set_invalidator_client_id(session->context()->invalidator_client_id());
    364 
    365   syncable::Directory* dir = session->context()->directory();
    366 
    367   LogClientToServerMessage(*msg);
    368   session->context()->traffic_recorder()->RecordClientToServerMessage(*msg);
    369   if (!PostAndProcessHeaders(session->context()->connection_manager(), session,
    370                              *msg, response)) {
    371     // There was an error establishing communication with the server.
    372     // We can not proceed beyond this point.
    373     const HttpResponse::ServerConnectionCode server_status =
    374         session->context()->connection_manager()->server_status();
    375 
    376     DCHECK_NE(server_status, HttpResponse::NONE);
    377     DCHECK_NE(server_status, HttpResponse::SERVER_CONNECTION_OK);
    378 
    379     return ServerConnectionErrorAsSyncerError(server_status);
    380   }
    381 
    382   LogClientToServerResponse(*response);
    383   session->context()->traffic_recorder()->RecordClientToServerResponse(
    384       *response);
    385 
    386   // Persist a bag of chips if it has been sent by the server.
    387   PersistBagOfChips(dir, *response);
    388 
    389   SyncProtocolError sync_protocol_error;
    390 
    391   // The DISABLED_BY_ADMIN error overrides other errors sent by the server.
    392   if (IsSyncDisabledByAdmin(*response)) {
    393     sync_protocol_error.error_type = DISABLED_BY_ADMIN;
    394     sync_protocol_error.action = STOP_SYNC_FOR_DISABLED_ACCOUNT;
    395   } else if (!VerifyResponseBirthday(*response, dir)) {
    396     // If sync isn't disabled, first check for a birthday mismatch error.
    397     sync_protocol_error.error_type = NOT_MY_BIRTHDAY;
    398     sync_protocol_error.action = DISABLE_SYNC_ON_CLIENT;
    399   } else if (response->has_error()) {
    400     // This is a new server. Just get the error from the protocol.
    401     sync_protocol_error = ConvertErrorPBToLocalType(response->error());
    402   } else {
    403     // Legacy server implementation. Compute the error based on |error_code|.
    404     sync_protocol_error = ConvertLegacyErrorCodeToNewError(
    405         response->error_code());
    406   }
    407 
    408   // Now set the error into the status so the layers above us could read it.
    409   sessions::StatusController* status = session->mutable_status_controller();
    410   status->set_sync_protocol_error(sync_protocol_error);
    411 
    412   // Inform the delegate of the error we got.
    413   session->delegate()->OnSyncProtocolError(session->TakeSnapshot());
    414 
    415   // Update our state for any other commands we've received.
    416   if (response->has_client_command()) {
    417     const sync_pb::ClientCommand& command = response->client_command();
    418     if (command.has_max_commit_batch_size()) {
    419       session->context()->set_max_commit_batch_size(
    420           command.max_commit_batch_size());
    421     }
    422 
    423     if (command.has_set_sync_long_poll_interval()) {
    424       session->delegate()->OnReceivedLongPollIntervalUpdate(
    425           base::TimeDelta::FromSeconds(command.set_sync_long_poll_interval()));
    426     }
    427 
    428     if (command.has_set_sync_poll_interval()) {
    429       session->delegate()->OnReceivedShortPollIntervalUpdate(
    430           base::TimeDelta::FromSeconds(command.set_sync_poll_interval()));
    431     }
    432 
    433     if (command.has_sessions_commit_delay_seconds()) {
    434       session->delegate()->OnReceivedSessionsCommitDelay(
    435           base::TimeDelta::FromSeconds(
    436               command.sessions_commit_delay_seconds()));
    437     }
    438 
    439     if (command.has_client_invalidation_hint_buffer_size()) {
    440       session->delegate()->OnReceivedClientInvalidationHintBufferSize(
    441           command.client_invalidation_hint_buffer_size());
    442     }
    443   }
    444 
    445   // Now do any special handling for the error type and decide on the return
    446   // value.
    447   switch (sync_protocol_error.error_type) {
    448     case UNKNOWN_ERROR:
    449       LOG(WARNING) << "Sync protocol out-of-date. The server is using a more "
    450                    << "recent version.";
    451       return SERVER_RETURN_UNKNOWN_ERROR;
    452     case SYNC_SUCCESS:
    453       LogResponseProfilingData(*response);
    454       return SYNCER_OK;
    455     case THROTTLED:
    456       if (sync_protocol_error.error_data_types.Empty()) {
    457         DLOG(WARNING) << "Client fully throttled by syncer.";
    458         session->delegate()->OnThrottled(GetThrottleDelay(*response));
    459       } else {
    460         DLOG(WARNING) << "Some types throttled by syncer.";
    461         session->delegate()->OnTypesThrottled(
    462             sync_protocol_error.error_data_types,
    463             GetThrottleDelay(*response));
    464       }
    465       return SERVER_RETURN_THROTTLED;
    466     case TRANSIENT_ERROR:
    467       return SERVER_RETURN_TRANSIENT_ERROR;
    468     case MIGRATION_DONE:
    469       LOG_IF(ERROR, 0 >= response->migrated_data_type_id_size())
    470           << "MIGRATION_DONE but no types specified.";
    471       // TODO(akalin): This should be a set union.
    472       session->mutable_status_controller()->
    473           set_types_needing_local_migration(GetTypesToMigrate(*response));
    474       return SERVER_RETURN_MIGRATION_DONE;
    475     case CLEAR_PENDING:
    476       return SERVER_RETURN_CLEAR_PENDING;
    477     case NOT_MY_BIRTHDAY:
    478       return SERVER_RETURN_NOT_MY_BIRTHDAY;
    479     case DISABLED_BY_ADMIN:
    480       return SERVER_RETURN_DISABLED_BY_ADMIN;
    481     default:
    482       NOTREACHED();
    483       return UNSET;
    484   }
    485 }
    486 
    487 // static
    488 bool SyncerProtoUtil::Compare(const syncable::Entry& local_entry,
    489                               const sync_pb::SyncEntity& server_entry) {
    490   const std::string name = NameFromSyncEntity(server_entry);
    491 
    492   CHECK_EQ(local_entry.Get(ID), SyncableIdFromProto(server_entry.id_string()));
    493   CHECK_EQ(server_entry.version(), local_entry.Get(BASE_VERSION));
    494   CHECK(!local_entry.Get(IS_UNSYNCED));
    495 
    496   if (local_entry.Get(IS_DEL) && server_entry.deleted())
    497     return true;
    498   if (local_entry.Get(CTIME) != ProtoTimeToTime(server_entry.ctime())) {
    499     LOG(WARNING) << "ctime mismatch";
    500     return false;
    501   }
    502 
    503   // These checks are somewhat prolix, but they're easier to debug than a big
    504   // boolean statement.
    505   string client_name = local_entry.Get(syncable::NON_UNIQUE_NAME);
    506   if (client_name != name) {
    507     LOG(WARNING) << "Client name mismatch";
    508     return false;
    509   }
    510   if (local_entry.Get(PARENT_ID) !=
    511       SyncableIdFromProto(server_entry.parent_id_string())) {
    512     LOG(WARNING) << "Parent ID mismatch";
    513     return false;
    514   }
    515   if (local_entry.Get(IS_DIR) != IsFolder(server_entry)) {
    516     LOG(WARNING) << "Dir field mismatch";
    517     return false;
    518   }
    519   if (local_entry.Get(IS_DEL) != server_entry.deleted()) {
    520     LOG(WARNING) << "Deletion mismatch";
    521     return false;
    522   }
    523   if (!local_entry.Get(IS_DIR) &&
    524       (local_entry.Get(MTIME) != ProtoTimeToTime(server_entry.mtime()))) {
    525     LOG(WARNING) << "mtime mismatch";
    526     return false;
    527   }
    528 
    529   return true;
    530 }
    531 
    532 // static
    533 bool SyncerProtoUtil::ShouldMaintainPosition(
    534     const sync_pb::SyncEntity& sync_entity) {
    535   // Maintain positions for bookmarks that are not server-defined top-level
    536   // folders.
    537   return GetModelType(sync_entity) == BOOKMARKS
    538       && !(sync_entity.folder() &&
    539            !sync_entity.server_defined_unique_tag().empty());
    540 }
    541 
    542 // static
    543 void SyncerProtoUtil::CopyProtoBytesIntoBlob(const std::string& proto_bytes,
    544                                              syncable::Blob* blob) {
    545   syncable::Blob proto_blob(proto_bytes.begin(), proto_bytes.end());
    546   blob->swap(proto_blob);
    547 }
    548 
    549 // static
    550 bool SyncerProtoUtil::ProtoBytesEqualsBlob(const std::string& proto_bytes,
    551                                            const syncable::Blob& blob) {
    552   if (proto_bytes.size() != blob.size())
    553     return false;
    554   return std::equal(proto_bytes.begin(), proto_bytes.end(), blob.begin());
    555 }
    556 
    557 // static
    558 void SyncerProtoUtil::CopyBlobIntoProtoBytes(const syncable::Blob& blob,
    559                                              std::string* proto_bytes) {
    560   std::string blob_string(blob.begin(), blob.end());
    561   proto_bytes->swap(blob_string);
    562 }
    563 
    564 // static
    565 const std::string& SyncerProtoUtil::NameFromSyncEntity(
    566     const sync_pb::SyncEntity& entry) {
    567   if (entry.has_non_unique_name())
    568     return entry.non_unique_name();
    569   return entry.name();
    570 }
    571 
    572 // static
    573 const std::string& SyncerProtoUtil::NameFromCommitEntryResponse(
    574     const sync_pb::CommitResponse_EntryResponse& entry) {
    575   if (entry.has_non_unique_name())
    576     return entry.non_unique_name();
    577   return entry.name();
    578 }
    579 
    580 // static
    581 void SyncerProtoUtil::PersistBagOfChips(syncable::Directory* dir,
    582     const sync_pb::ClientToServerResponse& response) {
    583   if (!response.has_new_bag_of_chips())
    584     return;
    585   std::string bag_of_chips;
    586   if (response.new_bag_of_chips().SerializeToString(&bag_of_chips))
    587     dir->set_bag_of_chips(bag_of_chips);
    588 }
    589 
    590 std::string SyncerProtoUtil::SyncEntityDebugString(
    591     const sync_pb::SyncEntity& entry) {
    592   const std::string& mtime_str =
    593       GetTimeDebugString(ProtoTimeToTime(entry.mtime()));
    594   const std::string& ctime_str =
    595       GetTimeDebugString(ProtoTimeToTime(entry.ctime()));
    596   return base::StringPrintf(
    597       "id: %s, parent_id: %s, "
    598       "version: %" PRId64"d, "
    599       "mtime: %" PRId64"d (%s), "
    600       "ctime: %" PRId64"d (%s), "
    601       "name: %s, sync_timestamp: %" PRId64"d, "
    602       "%s ",
    603       entry.id_string().c_str(),
    604       entry.parent_id_string().c_str(),
    605       entry.version(),
    606       entry.mtime(), mtime_str.c_str(),
    607       entry.ctime(), ctime_str.c_str(),
    608       entry.name().c_str(), entry.sync_timestamp(),
    609       entry.deleted() ? "deleted, ":"");
    610 }
    611 
    612 namespace {
    613 std::string GetUpdatesResponseString(
    614     const sync_pb::GetUpdatesResponse& response) {
    615   std::string output;
    616   output.append("GetUpdatesResponse:\n");
    617   for (int i = 0; i < response.entries_size(); i++) {
    618     output.append(SyncerProtoUtil::SyncEntityDebugString(response.entries(i)));
    619     output.append("\n");
    620   }
    621   return output;
    622 }
    623 }  // namespace
    624 
    625 std::string SyncerProtoUtil::ClientToServerResponseDebugString(
    626     const ClientToServerResponse& response) {
    627   // Add more handlers as needed.
    628   std::string output;
    629   if (response.has_get_updates())
    630     output.append(GetUpdatesResponseString(response.get_updates()));
    631   return output;
    632 }
    633 
    634 }  // namespace syncer
    635