Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / sync / engine / syncer_proto_util.cc
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   if (response->ParseFromString(params.buffer_out)) {
280     // TODO(tim): This is an egregious layering violation (bug 35060).
281     switch (response->error_code()) {
282       case sync_pb::SyncEnums::ACCESS_DENIED:
283       case sync_pb::SyncEnums::AUTH_INVALID:
284       case sync_pb::SyncEnums::USER_NOT_ACTIVATED:
285         // Fires on ScopedServerStatusWatcher
286         params.response.server_status = HttpResponse::SYNC_AUTH_ERROR;
287         return false;
288       default:
289         return true;
290     }
291   }
292
293   return false;
294 }
295
296 base::TimeDelta SyncerProtoUtil::GetThrottleDelay(
297     const ClientToServerResponse& response) {
298   base::TimeDelta throttle_delay =
299       base::TimeDelta::FromSeconds(kSyncDelayAfterThrottled);
300   if (response.has_client_command()) {
301     const sync_pb::ClientCommand& command = response.client_command();
302     if (command.has_throttle_delay_seconds()) {
303       throttle_delay =
304           base::TimeDelta::FromSeconds(command.throttle_delay_seconds());
305     }
306   }
307   return throttle_delay;
308 }
309
310 namespace {
311
312 // Helper function for an assertion in PostClientToServerMessage.
313 bool IsVeryFirstGetUpdates(const ClientToServerMessage& message) {
314   if (!message.has_get_updates())
315     return false;
316   DCHECK_LT(0, message.get_updates().from_progress_marker_size());
317   for (int i = 0; i < message.get_updates().from_progress_marker_size(); ++i) {
318     if (!message.get_updates().from_progress_marker(i).token().empty())
319       return false;
320   }
321   return true;
322 }
323
324 // TODO(lipalani) : Rename these function names as per the CR for issue 7740067.
325 SyncProtocolError ConvertLegacyErrorCodeToNewError(
326     const sync_pb::SyncEnums::ErrorType& error_type) {
327   SyncProtocolError error;
328   error.error_type = ConvertSyncProtocolErrorTypePBToLocalType(error_type);
329   if (error_type == sync_pb::SyncEnums::CLEAR_PENDING ||
330       error_type == sync_pb::SyncEnums::NOT_MY_BIRTHDAY) {
331     error.action = DISABLE_SYNC_ON_CLIENT;
332   } else if (error_type == sync_pb::SyncEnums::DISABLED_BY_ADMIN) {
333     error.action = STOP_SYNC_FOR_DISABLED_ACCOUNT;
334   }  // There is no other action we can compute for legacy server.
335   return error;
336 }
337
338 }  // namespace
339
340 // static
341 SyncerError SyncerProtoUtil::PostClientToServerMessage(
342     ClientToServerMessage* msg,
343     ClientToServerResponse* response,
344     SyncSession* session) {
345   CHECK(response);
346   DCHECK(!msg->get_updates().has_from_timestamp());  // Deprecated.
347   DCHECK(!msg->get_updates().has_requested_types());  // Deprecated.
348
349   // Add must-have fields.
350   SetProtocolVersion(msg);
351   AddRequestBirthday(session->context()->directory(), msg);
352   DCHECK(msg->has_store_birthday() || IsVeryFirstGetUpdates(*msg));
353   AddBagOfChips(session->context()->directory(), msg);
354   msg->set_api_key(google_apis::GetAPIKey());
355   msg->mutable_client_status()->CopyFrom(session->context()->client_status());
356   msg->set_invalidator_client_id(session->context()->invalidator_client_id());
357
358   syncable::Directory* dir = session->context()->directory();
359
360   LogClientToServerMessage(*msg);
361   session->context()->traffic_recorder()->RecordClientToServerMessage(*msg);
362   if (!PostAndProcessHeaders(session->context()->connection_manager(), session,
363                              *msg, response)) {
364     // There was an error establishing communication with the server.
365     // We can not proceed beyond this point.
366     const HttpResponse::ServerConnectionCode server_status =
367         session->context()->connection_manager()->server_status();
368
369     DCHECK_NE(server_status, HttpResponse::NONE);
370     DCHECK_NE(server_status, HttpResponse::SERVER_CONNECTION_OK);
371
372     return ServerConnectionErrorAsSyncerError(server_status);
373   }
374
375   LogClientToServerResponse(*response);
376   session->context()->traffic_recorder()->RecordClientToServerResponse(
377       *response);
378
379   // Persist a bag of chips if it has been sent by the server.
380   PersistBagOfChips(dir, *response);
381
382   SyncProtocolError sync_protocol_error;
383
384   // The DISABLED_BY_ADMIN error overrides other errors sent by the server.
385   if (IsSyncDisabledByAdmin(*response)) {
386     sync_protocol_error.error_type = DISABLED_BY_ADMIN;
387     sync_protocol_error.action = STOP_SYNC_FOR_DISABLED_ACCOUNT;
388   } else if (!VerifyResponseBirthday(*response, dir)) {
389     // If sync isn't disabled, first check for a birthday mismatch error.
390     sync_protocol_error.error_type = NOT_MY_BIRTHDAY;
391     sync_protocol_error.action = DISABLE_SYNC_ON_CLIENT;
392   } else if (response->has_error()) {
393     // This is a new server. Just get the error from the protocol.
394     sync_protocol_error = ConvertErrorPBToLocalType(response->error());
395   } else {
396     // Legacy server implementation. Compute the error based on |error_code|.
397     sync_protocol_error = ConvertLegacyErrorCodeToNewError(
398         response->error_code());
399   }
400
401   // Inform the delegate of the error we got.
402   session->delegate()->OnSyncProtocolError(sync_protocol_error);
403
404   // Update our state for any other commands we've received.
405   if (response->has_client_command()) {
406     const sync_pb::ClientCommand& command = response->client_command();
407     if (command.has_max_commit_batch_size()) {
408       session->context()->set_max_commit_batch_size(
409           command.max_commit_batch_size());
410     }
411
412     if (command.has_set_sync_long_poll_interval()) {
413       session->delegate()->OnReceivedLongPollIntervalUpdate(
414           base::TimeDelta::FromSeconds(command.set_sync_long_poll_interval()));
415     }
416
417     if (command.has_set_sync_poll_interval()) {
418       session->delegate()->OnReceivedShortPollIntervalUpdate(
419           base::TimeDelta::FromSeconds(command.set_sync_poll_interval()));
420     }
421
422     if (command.has_sessions_commit_delay_seconds()) {
423       session->delegate()->OnReceivedSessionsCommitDelay(
424           base::TimeDelta::FromSeconds(
425               command.sessions_commit_delay_seconds()));
426     }
427
428     if (command.has_client_invalidation_hint_buffer_size()) {
429       session->delegate()->OnReceivedClientInvalidationHintBufferSize(
430           command.client_invalidation_hint_buffer_size());
431     }
432
433     if (command.has_gu_retry_delay_seconds()) {
434       session->delegate()->OnReceivedGuRetryDelay(
435           base::TimeDelta::FromSeconds(command.gu_retry_delay_seconds()));
436     }
437   }
438
439   // Now do any special handling for the error type and decide on the return
440   // value.
441   switch (sync_protocol_error.error_type) {
442     case UNKNOWN_ERROR:
443       LOG(WARNING) << "Sync protocol out-of-date. The server is using a more "
444                    << "recent version.";
445       return SERVER_RETURN_UNKNOWN_ERROR;
446     case SYNC_SUCCESS:
447       LogResponseProfilingData(*response);
448       return SYNCER_OK;
449     case THROTTLED:
450       if (sync_protocol_error.error_data_types.Empty()) {
451         DLOG(WARNING) << "Client fully throttled by syncer.";
452         session->delegate()->OnThrottled(GetThrottleDelay(*response));
453       } else {
454         DLOG(WARNING) << "Some types throttled by syncer.";
455         session->delegate()->OnTypesThrottled(
456             sync_protocol_error.error_data_types,
457             GetThrottleDelay(*response));
458       }
459       return SERVER_RETURN_THROTTLED;
460     case TRANSIENT_ERROR:
461       return SERVER_RETURN_TRANSIENT_ERROR;
462     case MIGRATION_DONE:
463       LOG_IF(ERROR, 0 >= response->migrated_data_type_id_size())
464           << "MIGRATION_DONE but no types specified.";
465       session->delegate()->OnReceivedMigrationRequest(
466           GetTypesToMigrate(*response));
467       return SERVER_RETURN_MIGRATION_DONE;
468     case CLEAR_PENDING:
469       return SERVER_RETURN_CLEAR_PENDING;
470     case NOT_MY_BIRTHDAY:
471       return SERVER_RETURN_NOT_MY_BIRTHDAY;
472     case DISABLED_BY_ADMIN:
473       return SERVER_RETURN_DISABLED_BY_ADMIN;
474     default:
475       NOTREACHED();
476       return UNSET;
477   }
478 }
479
480 // static
481 bool SyncerProtoUtil::ShouldMaintainPosition(
482     const sync_pb::SyncEntity& sync_entity) {
483   // Maintain positions for bookmarks that are not server-defined top-level
484   // folders.
485   return GetModelType(sync_entity) == BOOKMARKS
486       && !(sync_entity.folder() &&
487            !sync_entity.server_defined_unique_tag().empty());
488 }
489
490 // static
491 void SyncerProtoUtil::CopyProtoBytesIntoBlob(const std::string& proto_bytes,
492                                              syncable::Blob* blob) {
493   syncable::Blob proto_blob(proto_bytes.begin(), proto_bytes.end());
494   blob->swap(proto_blob);
495 }
496
497 // static
498 bool SyncerProtoUtil::ProtoBytesEqualsBlob(const std::string& proto_bytes,
499                                            const syncable::Blob& blob) {
500   if (proto_bytes.size() != blob.size())
501     return false;
502   return std::equal(proto_bytes.begin(), proto_bytes.end(), blob.begin());
503 }
504
505 // static
506 void SyncerProtoUtil::CopyBlobIntoProtoBytes(const syncable::Blob& blob,
507                                              std::string* proto_bytes) {
508   std::string blob_string(blob.begin(), blob.end());
509   proto_bytes->swap(blob_string);
510 }
511
512 // static
513 const std::string& SyncerProtoUtil::NameFromSyncEntity(
514     const sync_pb::SyncEntity& entry) {
515   if (entry.has_non_unique_name())
516     return entry.non_unique_name();
517   return entry.name();
518 }
519
520 // static
521 const std::string& SyncerProtoUtil::NameFromCommitEntryResponse(
522     const sync_pb::CommitResponse_EntryResponse& entry) {
523   if (entry.has_non_unique_name())
524     return entry.non_unique_name();
525   return entry.name();
526 }
527
528 // static
529 void SyncerProtoUtil::PersistBagOfChips(syncable::Directory* dir,
530     const sync_pb::ClientToServerResponse& response) {
531   if (!response.has_new_bag_of_chips())
532     return;
533   std::string bag_of_chips;
534   if (response.new_bag_of_chips().SerializeToString(&bag_of_chips))
535     dir->set_bag_of_chips(bag_of_chips);
536 }
537
538 std::string SyncerProtoUtil::SyncEntityDebugString(
539     const sync_pb::SyncEntity& entry) {
540   const std::string& mtime_str =
541       GetTimeDebugString(ProtoTimeToTime(entry.mtime()));
542   const std::string& ctime_str =
543       GetTimeDebugString(ProtoTimeToTime(entry.ctime()));
544   return base::StringPrintf(
545       "id: %s, parent_id: %s, "
546       "version: %" PRId64"d, "
547       "mtime: %" PRId64"d (%s), "
548       "ctime: %" PRId64"d (%s), "
549       "name: %s, sync_timestamp: %" PRId64"d, "
550       "%s ",
551       entry.id_string().c_str(),
552       entry.parent_id_string().c_str(),
553       entry.version(),
554       entry.mtime(), mtime_str.c_str(),
555       entry.ctime(), ctime_str.c_str(),
556       entry.name().c_str(), entry.sync_timestamp(),
557       entry.deleted() ? "deleted, ":"");
558 }
559
560 namespace {
561 std::string GetUpdatesResponseString(
562     const sync_pb::GetUpdatesResponse& response) {
563   std::string output;
564   output.append("GetUpdatesResponse:\n");
565   for (int i = 0; i < response.entries_size(); i++) {
566     output.append(SyncerProtoUtil::SyncEntityDebugString(response.entries(i)));
567     output.append("\n");
568   }
569   return output;
570 }
571 }  // namespace
572
573 std::string SyncerProtoUtil::ClientToServerResponseDebugString(
574     const ClientToServerResponse& response) {
575   // Add more handlers as needed.
576   std::string output;
577   if (response.has_get_updates())
578     output.append(GetUpdatesResponseString(response.get_updates()));
579   return output;
580 }
581
582 }  // namespace syncer