| OLD | NEW |
| (Empty) |
| 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 <stdint.h> | |
| 6 | |
| 7 #include <cstddef> | |
| 8 #include <cstdio> | |
| 9 #include <memory> | |
| 10 #include <string> | |
| 11 #include <utility> | |
| 12 | |
| 13 #include "base/at_exit.h" | |
| 14 #include "base/command_line.h" | |
| 15 #include "base/compiler_specific.h" | |
| 16 #include "base/debug/stack_trace.h" | |
| 17 #include "base/files/scoped_temp_dir.h" | |
| 18 #include "base/json/json_writer.h" | |
| 19 #include "base/logging.h" | |
| 20 #include "base/memory/ptr_util.h" | |
| 21 #include "base/memory/ref_counted.h" | |
| 22 #include "base/memory/weak_ptr.h" | |
| 23 #include "base/message_loop/message_loop.h" | |
| 24 #include "base/rand_util.h" | |
| 25 #include "base/run_loop.h" | |
| 26 #include "base/task_runner.h" | |
| 27 #include "base/threading/thread.h" | |
| 28 #include "build/build_config.h" | |
| 29 #include "components/invalidation/impl/non_blocking_invalidator.h" | |
| 30 #include "components/invalidation/public/object_id_invalidation_map.h" | |
| 31 #include "components/sync_driver/invalidation_helper.h" | |
| 32 #include "jingle/notifier/base/notification_method.h" | |
| 33 #include "jingle/notifier/base/notifier_options.h" | |
| 34 #include "net/base/host_port_pair.h" | |
| 35 #include "net/base/network_change_notifier.h" | |
| 36 #include "net/dns/host_resolver.h" | |
| 37 #include "net/http/transport_security_state.h" | |
| 38 #include "net/url_request/url_request_test_util.h" | |
| 39 #include "sync/internal_api/public/base/cancelation_signal.h" | |
| 40 #include "sync/internal_api/public/base/model_type.h" | |
| 41 #include "sync/internal_api/public/base_node.h" | |
| 42 #include "sync/internal_api/public/engine/passive_model_worker.h" | |
| 43 #include "sync/internal_api/public/http_bridge.h" | |
| 44 #include "sync/internal_api/public/http_post_provider_factory.h" | |
| 45 #include "sync/internal_api/public/internal_components_factory_impl.h" | |
| 46 #include "sync/internal_api/public/read_node.h" | |
| 47 #include "sync/internal_api/public/sync_manager.h" | |
| 48 #include "sync/internal_api/public/sync_manager_factory.h" | |
| 49 #include "sync/internal_api/public/util/unrecoverable_error_handler.h" | |
| 50 #include "sync/internal_api/public/util/weak_handle.h" | |
| 51 #include "sync/js/js_event_details.h" | |
| 52 #include "sync/js/js_event_handler.h" | |
| 53 #include "sync/test/fake_encryptor.h" | |
| 54 #include "sync/tools/null_invalidation_state_tracker.h" | |
| 55 #include "url/gurl.h" | |
| 56 | |
| 57 #if defined(OS_MACOSX) | |
| 58 #include "base/mac/scoped_nsautorelease_pool.h" | |
| 59 #endif | |
| 60 | |
| 61 // This is a simple utility that initializes a sync client and | |
| 62 // prints out any events. | |
| 63 | |
| 64 // TODO(akalin): Refactor to combine shared code with | |
| 65 // sync_listen_notifications. | |
| 66 namespace syncer { | |
| 67 namespace { | |
| 68 | |
| 69 const char kEmailSwitch[] = "email"; | |
| 70 const char kTokenSwitch[] = "token"; | |
| 71 const char kXmppHostPortSwitch[] = "xmpp-host-port"; | |
| 72 const char kXmppTrySslTcpFirstSwitch[] = "xmpp-try-ssltcp-first"; | |
| 73 const char kXmppAllowInsecureConnectionSwitch[] = | |
| 74 "xmpp-allow-insecure-connection"; | |
| 75 const char kSyncServiceURL[] = "https://clients4.google.com/chrome-sync/dev"; | |
| 76 | |
| 77 // Needed to use a real host resolver. | |
| 78 class MyTestURLRequestContext : public net::TestURLRequestContext { | |
| 79 public: | |
| 80 MyTestURLRequestContext() : TestURLRequestContext(true) { | |
| 81 context_storage_.set_host_resolver( | |
| 82 net::HostResolver::CreateDefaultResolver(NULL)); | |
| 83 context_storage_.set_transport_security_state( | |
| 84 base::WrapUnique(new net::TransportSecurityState())); | |
| 85 Init(); | |
| 86 } | |
| 87 | |
| 88 ~MyTestURLRequestContext() override {} | |
| 89 }; | |
| 90 | |
| 91 class MyTestURLRequestContextGetter : public net::TestURLRequestContextGetter { | |
| 92 public: | |
| 93 explicit MyTestURLRequestContextGetter( | |
| 94 const scoped_refptr<base::SingleThreadTaskRunner>& io_task_runner) | |
| 95 : TestURLRequestContextGetter(io_task_runner) {} | |
| 96 | |
| 97 net::TestURLRequestContext* GetURLRequestContext() override { | |
| 98 // Construct |context_| lazily so it gets constructed on the right | |
| 99 // thread (the IO thread). | |
| 100 if (!context_) | |
| 101 context_.reset(new MyTestURLRequestContext()); | |
| 102 return context_.get(); | |
| 103 } | |
| 104 | |
| 105 private: | |
| 106 ~MyTestURLRequestContextGetter() override {} | |
| 107 | |
| 108 std::unique_ptr<MyTestURLRequestContext> context_; | |
| 109 }; | |
| 110 | |
| 111 // TODO(akalin): Use system encryptor once it's moved to sync/. | |
| 112 class NullEncryptor : public Encryptor { | |
| 113 public: | |
| 114 ~NullEncryptor() override {} | |
| 115 | |
| 116 bool EncryptString(const std::string& plaintext, | |
| 117 std::string* ciphertext) override { | |
| 118 *ciphertext = plaintext; | |
| 119 return true; | |
| 120 } | |
| 121 | |
| 122 bool DecryptString(const std::string& ciphertext, | |
| 123 std::string* plaintext) override { | |
| 124 *plaintext = ciphertext; | |
| 125 return true; | |
| 126 } | |
| 127 }; | |
| 128 | |
| 129 std::string ValueToString(const base::Value& value) { | |
| 130 std::string str; | |
| 131 base::JSONWriter::Write(value, &str); | |
| 132 return str; | |
| 133 } | |
| 134 | |
| 135 class LoggingChangeDelegate : public SyncManager::ChangeDelegate { | |
| 136 public: | |
| 137 ~LoggingChangeDelegate() override {} | |
| 138 | |
| 139 void OnChangesApplied(ModelType model_type, | |
| 140 int64_t model_version, | |
| 141 const BaseTransaction* trans, | |
| 142 const ImmutableChangeRecordList& changes) override { | |
| 143 LOG(INFO) << "Changes applied for " | |
| 144 << ModelTypeToString(model_type); | |
| 145 size_t i = 1; | |
| 146 size_t change_count = changes.Get().size(); | |
| 147 for (ChangeRecordList::const_iterator it = | |
| 148 changes.Get().begin(); it != changes.Get().end(); ++it) { | |
| 149 std::unique_ptr<base::DictionaryValue> change_value(it->ToValue()); | |
| 150 LOG(INFO) << "Change (" << i << "/" << change_count << "): " | |
| 151 << ValueToString(*change_value); | |
| 152 if (it->action != ChangeRecord::ACTION_DELETE) { | |
| 153 ReadNode node(trans); | |
| 154 CHECK_EQ(node.InitByIdLookup(it->id), BaseNode::INIT_OK); | |
| 155 std::unique_ptr<base::DictionaryValue> details(node.ToValue()); | |
| 156 VLOG(1) << "Details: " << ValueToString(*details); | |
| 157 } | |
| 158 ++i; | |
| 159 } | |
| 160 } | |
| 161 | |
| 162 void OnChangesComplete(ModelType model_type) override { | |
| 163 LOG(INFO) << "Changes complete for " | |
| 164 << ModelTypeToString(model_type); | |
| 165 } | |
| 166 }; | |
| 167 | |
| 168 class LoggingUnrecoverableErrorHandler | |
| 169 : public UnrecoverableErrorHandler { | |
| 170 public: | |
| 171 ~LoggingUnrecoverableErrorHandler() override {} | |
| 172 | |
| 173 void OnUnrecoverableError(const tracked_objects::Location& from_here, | |
| 174 const std::string& message) override { | |
| 175 if (LOG_IS_ON(ERROR)) { | |
| 176 logging::LogMessage(from_here.file_name(), from_here.line_number(), | |
| 177 logging::LOG_ERROR).stream() | |
| 178 << message; | |
| 179 } | |
| 180 } | |
| 181 }; | |
| 182 | |
| 183 class LoggingJsEventHandler | |
| 184 : public JsEventHandler, | |
| 185 public base::SupportsWeakPtr<LoggingJsEventHandler> { | |
| 186 public: | |
| 187 ~LoggingJsEventHandler() override {} | |
| 188 | |
| 189 void HandleJsEvent(const std::string& name, | |
| 190 const JsEventDetails& details) override { | |
| 191 VLOG(1) << name << ": " << details.ToString(); | |
| 192 } | |
| 193 }; | |
| 194 | |
| 195 class InvalidationAdapter : public syncer::InvalidationInterface { | |
| 196 public: | |
| 197 explicit InvalidationAdapter(const syncer::Invalidation& invalidation) | |
| 198 : invalidation_(invalidation) {} | |
| 199 ~InvalidationAdapter() override {} | |
| 200 | |
| 201 bool IsUnknownVersion() const override { | |
| 202 return invalidation_.is_unknown_version(); | |
| 203 } | |
| 204 | |
| 205 const std::string& GetPayload() const override { | |
| 206 return invalidation_.payload(); | |
| 207 } | |
| 208 | |
| 209 int64_t GetVersion() const override { return invalidation_.version(); } | |
| 210 | |
| 211 void Acknowledge() override { invalidation_.Acknowledge(); } | |
| 212 | |
| 213 void Drop() override { invalidation_.Drop(); } | |
| 214 | |
| 215 private: | |
| 216 syncer::Invalidation invalidation_; | |
| 217 }; | |
| 218 | |
| 219 class InvalidatorShim : public InvalidationHandler { | |
| 220 public: | |
| 221 explicit InvalidatorShim(SyncManager* sync_manager) | |
| 222 : sync_manager_(sync_manager) {} | |
| 223 | |
| 224 void OnInvalidatorStateChange(InvalidatorState state) override { | |
| 225 sync_manager_->SetInvalidatorEnabled(state == INVALIDATIONS_ENABLED); | |
| 226 } | |
| 227 | |
| 228 void OnIncomingInvalidation( | |
| 229 const ObjectIdInvalidationMap& invalidation_map) override { | |
| 230 syncer::ObjectIdSet ids = invalidation_map.GetObjectIds(); | |
| 231 for (syncer::ObjectIdSet::const_iterator ids_it = ids.begin(); | |
| 232 ids_it != ids.end(); | |
| 233 ++ids_it) { | |
| 234 syncer::ModelType type; | |
| 235 if (!NotificationTypeToRealModelType(ids_it->name(), &type)) { | |
| 236 DLOG(WARNING) << "Notification has invalid id: " | |
| 237 << syncer::ObjectIdToString(*ids_it); | |
| 238 } else { | |
| 239 syncer::SingleObjectInvalidationSet invalidation_set = | |
| 240 invalidation_map.ForObject(*ids_it); | |
| 241 for (syncer::SingleObjectInvalidationSet::const_iterator inv_it = | |
| 242 invalidation_set.begin(); | |
| 243 inv_it != invalidation_set.end(); | |
| 244 ++inv_it) { | |
| 245 std::unique_ptr<syncer::InvalidationInterface> inv_adapter( | |
| 246 new InvalidationAdapter(*inv_it)); | |
| 247 sync_manager_->OnIncomingInvalidation(type, std::move(inv_adapter)); | |
| 248 } | |
| 249 } | |
| 250 } | |
| 251 } | |
| 252 | |
| 253 std::string GetOwnerName() const override { return "InvalidatorShim"; } | |
| 254 | |
| 255 private: | |
| 256 SyncManager* sync_manager_; | |
| 257 }; | |
| 258 | |
| 259 void LogUnrecoverableErrorContext() { | |
| 260 base::debug::StackTrace().Print(); | |
| 261 } | |
| 262 | |
| 263 notifier::NotifierOptions ParseNotifierOptions( | |
| 264 const base::CommandLine& command_line, | |
| 265 const scoped_refptr<net::URLRequestContextGetter>& request_context_getter) { | |
| 266 notifier::NotifierOptions notifier_options; | |
| 267 notifier_options.request_context_getter = request_context_getter; | |
| 268 notifier_options.auth_mechanism = "X-OAUTH2"; | |
| 269 | |
| 270 if (command_line.HasSwitch(kXmppHostPortSwitch)) { | |
| 271 notifier_options.xmpp_host_port = | |
| 272 net::HostPortPair::FromString( | |
| 273 command_line.GetSwitchValueASCII(kXmppHostPortSwitch)); | |
| 274 LOG(INFO) << "Using " << notifier_options.xmpp_host_port.ToString() | |
| 275 << " for test sync notification server."; | |
| 276 } | |
| 277 | |
| 278 notifier_options.try_ssltcp_first = | |
| 279 command_line.HasSwitch(kXmppTrySslTcpFirstSwitch); | |
| 280 LOG_IF(INFO, notifier_options.try_ssltcp_first) | |
| 281 << "Trying SSL/TCP port before XMPP port for notifications."; | |
| 282 | |
| 283 notifier_options.allow_insecure_connection = | |
| 284 command_line.HasSwitch(kXmppAllowInsecureConnectionSwitch); | |
| 285 LOG_IF(INFO, notifier_options.allow_insecure_connection) | |
| 286 << "Allowing insecure XMPP connections."; | |
| 287 | |
| 288 return notifier_options; | |
| 289 } | |
| 290 | |
| 291 void StubNetworkTimeUpdateCallback(const base::Time&, | |
| 292 const base::TimeDelta&, | |
| 293 const base::TimeDelta&) { | |
| 294 } | |
| 295 | |
| 296 int SyncClientMain(int argc, char* argv[]) { | |
| 297 #if defined(OS_MACOSX) | |
| 298 base::mac::ScopedNSAutoreleasePool pool; | |
| 299 #endif | |
| 300 base::AtExitManager exit_manager; | |
| 301 base::CommandLine::Init(argc, argv); | |
| 302 logging::LoggingSettings settings; | |
| 303 settings.logging_dest = logging::LOG_TO_SYSTEM_DEBUG_LOG; | |
| 304 logging::InitLogging(settings); | |
| 305 | |
| 306 base::MessageLoop sync_loop; | |
| 307 base::Thread io_thread("IO thread"); | |
| 308 base::Thread::Options options; | |
| 309 options.message_loop_type = base::MessageLoop::TYPE_IO; | |
| 310 io_thread.StartWithOptions(options); | |
| 311 | |
| 312 // Parse command line. | |
| 313 const base::CommandLine& command_line = | |
| 314 *base::CommandLine::ForCurrentProcess(); | |
| 315 SyncCredentials credentials; | |
| 316 credentials.account_id = command_line.GetSwitchValueASCII(kEmailSwitch); | |
| 317 credentials.email = command_line.GetSwitchValueASCII(kEmailSwitch); | |
| 318 credentials.sync_token = command_line.GetSwitchValueASCII(kTokenSwitch); | |
| 319 // TODO(akalin): Write a wrapper script that gets a token for an | |
| 320 // email and password and passes that in to this utility. | |
| 321 if (credentials.email.empty() || credentials.sync_token.empty()) { | |
| 322 std::printf("Usage: %s --%s=foo@bar.com --%s=token\n" | |
| 323 "[--%s=host:port] [--%s] [--%s]\n" | |
| 324 "Run chrome and set a breakpoint on\n" | |
| 325 "syncer::SyncManagerImpl::UpdateCredentials() " | |
| 326 "after logging into\n" | |
| 327 "sync to get the token to pass into this utility.\n", | |
| 328 argv[0], | |
| 329 kEmailSwitch, kTokenSwitch, kXmppHostPortSwitch, | |
| 330 kXmppTrySslTcpFirstSwitch, | |
| 331 kXmppAllowInsecureConnectionSwitch); | |
| 332 return -1; | |
| 333 } | |
| 334 | |
| 335 // Set up objects that monitor the network. | |
| 336 std::unique_ptr<net::NetworkChangeNotifier> network_change_notifier( | |
| 337 net::NetworkChangeNotifier::Create()); | |
| 338 | |
| 339 // Set up sync notifier factory. | |
| 340 const scoped_refptr<MyTestURLRequestContextGetter> context_getter = | |
| 341 new MyTestURLRequestContextGetter(io_thread.task_runner()); | |
| 342 const notifier::NotifierOptions& notifier_options = | |
| 343 ParseNotifierOptions(command_line, context_getter); | |
| 344 syncer::NetworkChannelCreator network_channel_creator = | |
| 345 syncer::NonBlockingInvalidator::MakePushClientChannelCreator( | |
| 346 notifier_options); | |
| 347 const char kClientInfo[] = "standalone_sync_client"; | |
| 348 std::string invalidator_id = base::RandBytesAsString(8); | |
| 349 NullInvalidationStateTracker null_invalidation_state_tracker; | |
| 350 std::unique_ptr<Invalidator> invalidator(new NonBlockingInvalidator( | |
| 351 network_channel_creator, invalidator_id, | |
| 352 null_invalidation_state_tracker.GetSavedInvalidations(), | |
| 353 null_invalidation_state_tracker.GetBootstrapData(), | |
| 354 &null_invalidation_state_tracker, kClientInfo, | |
| 355 notifier_options.request_context_getter)); | |
| 356 | |
| 357 // Set up database directory for the syncer. | |
| 358 base::ScopedTempDir database_dir; | |
| 359 CHECK(database_dir.CreateUniqueTempDir()); | |
| 360 | |
| 361 // Developers often add types to ModelTypeSet::All() before the server | |
| 362 // supports them. We need to be explicit about which types we want here. | |
| 363 ModelTypeSet model_types; | |
| 364 model_types.Put(BOOKMARKS); | |
| 365 model_types.Put(PREFERENCES); | |
| 366 model_types.Put(PASSWORDS); | |
| 367 model_types.Put(AUTOFILL); | |
| 368 model_types.Put(THEMES); | |
| 369 model_types.Put(TYPED_URLS); | |
| 370 model_types.Put(EXTENSIONS); | |
| 371 model_types.Put(NIGORI); | |
| 372 model_types.Put(SEARCH_ENGINES); | |
| 373 model_types.Put(SESSIONS); | |
| 374 model_types.Put(APPS); | |
| 375 model_types.Put(AUTOFILL_PROFILE); | |
| 376 model_types.Put(APP_SETTINGS); | |
| 377 model_types.Put(EXTENSION_SETTINGS); | |
| 378 model_types.Put(APP_NOTIFICATIONS); | |
| 379 model_types.Put(HISTORY_DELETE_DIRECTIVES); | |
| 380 model_types.Put(SYNCED_NOTIFICATIONS); | |
| 381 model_types.Put(SYNCED_NOTIFICATION_APP_INFO); | |
| 382 model_types.Put(DEVICE_INFO); | |
| 383 model_types.Put(EXPERIMENTS); | |
| 384 model_types.Put(PRIORITY_PREFERENCES); | |
| 385 model_types.Put(DICTIONARY); | |
| 386 model_types.Put(FAVICON_IMAGES); | |
| 387 model_types.Put(FAVICON_TRACKING); | |
| 388 | |
| 389 ModelSafeRoutingInfo routing_info; | |
| 390 for (ModelTypeSet::Iterator it = model_types.First(); | |
| 391 it.Good(); it.Inc()) { | |
| 392 routing_info[it.Get()] = GROUP_PASSIVE; | |
| 393 } | |
| 394 scoped_refptr<PassiveModelWorker> passive_model_safe_worker = | |
| 395 new PassiveModelWorker(nullptr); | |
| 396 std::vector<scoped_refptr<ModelSafeWorker> > workers; | |
| 397 workers.push_back(passive_model_safe_worker); | |
| 398 | |
| 399 // Set up sync manager. | |
| 400 SyncManagerFactory sync_manager_factory; | |
| 401 std::unique_ptr<SyncManager> sync_manager = | |
| 402 sync_manager_factory.CreateSyncManager("sync_client manager"); | |
| 403 LoggingJsEventHandler js_event_handler; | |
| 404 // Used only by InitialProcessMetadata(), so it's okay to leave this as NULL. | |
| 405 const scoped_refptr<base::TaskRunner> blocking_task_runner = NULL; | |
| 406 const char kUserAgent[] = "sync_client"; | |
| 407 // TODO(akalin): Replace this with just the context getter once | |
| 408 // HttpPostProviderFactory is removed. | |
| 409 CancelationSignal factory_cancelation_signal; | |
| 410 std::unique_ptr<HttpPostProviderFactory> post_factory(new HttpBridgeFactory( | |
| 411 context_getter.get(), base::Bind(&StubNetworkTimeUpdateCallback), | |
| 412 &factory_cancelation_signal)); | |
| 413 post_factory->Init(kUserAgent, BindToTrackerCallback()); | |
| 414 // Used only when committing bookmarks, so it's okay to leave this | |
| 415 // as NULL. | |
| 416 ExtensionsActivity* extensions_activity = NULL; | |
| 417 LoggingChangeDelegate change_delegate; | |
| 418 const char kRestoredKeyForBootstrapping[] = ""; | |
| 419 const char kRestoredKeystoreKeyForBootstrapping[] = ""; | |
| 420 NullEncryptor null_encryptor; | |
| 421 InternalComponentsFactoryImpl::Switches factory_switches = { | |
| 422 InternalComponentsFactory::ENCRYPTION_KEYSTORE, | |
| 423 InternalComponentsFactory::BACKOFF_NORMAL | |
| 424 }; | |
| 425 CancelationSignal scm_cancelation_signal; | |
| 426 | |
| 427 SyncManager::InitArgs args; | |
| 428 args.database_location = database_dir.path(); | |
| 429 args.event_handler = WeakHandle<JsEventHandler>(js_event_handler.AsWeakPtr()); | |
| 430 args.service_url = GURL(kSyncServiceURL); | |
| 431 args.post_factory = std::move(post_factory); | |
| 432 args.workers = workers; | |
| 433 args.extensions_activity = extensions_activity; | |
| 434 args.change_delegate = &change_delegate; | |
| 435 args.credentials = credentials; | |
| 436 args.invalidator_client_id = invalidator_id; | |
| 437 args.restored_key_for_bootstrapping = kRestoredKeyForBootstrapping; | |
| 438 args.restored_keystore_key_for_bootstrapping = | |
| 439 kRestoredKeystoreKeyForBootstrapping; | |
| 440 args.internal_components_factory.reset( | |
| 441 new InternalComponentsFactoryImpl(factory_switches)); | |
| 442 args.encryptor = &null_encryptor; | |
| 443 args.unrecoverable_error_handler = WeakHandle<UnrecoverableErrorHandler>(); | |
| 444 args.report_unrecoverable_error_function = | |
| 445 base::Bind(LogUnrecoverableErrorContext); | |
| 446 args.cancelation_signal = &scm_cancelation_signal; | |
| 447 sync_manager->Init(&args); | |
| 448 // TODO(akalin): Avoid passing in model parameters multiple times by | |
| 449 // organizing handling of model types. | |
| 450 invalidator->UpdateCredentials(credentials.email, credentials.sync_token); | |
| 451 std::unique_ptr<InvalidatorShim> shim( | |
| 452 new InvalidatorShim(sync_manager.get())); | |
| 453 invalidator->RegisterHandler(shim.get()); | |
| 454 CHECK(invalidator->UpdateRegisteredIds( | |
| 455 shim.get(), ModelTypeSetToObjectIdSet(model_types))); | |
| 456 sync_manager->StartSyncingNormally(routing_info, base::Time()); | |
| 457 | |
| 458 base::RunLoop().Run(); | |
| 459 | |
| 460 io_thread.Stop(); | |
| 461 return 0; | |
| 462 } | |
| 463 | |
| 464 } // namespace | |
| 465 } // namespace syncer | |
| 466 | |
| 467 int main(int argc, char* argv[]) { | |
| 468 return syncer::SyncClientMain(argc, argv); | |
| 469 } | |
| OLD | NEW |