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