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/non_blocking_invalidator.h"
24 #include "components/invalidation/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/report_unrecoverable_error_function.h"
42 #include "sync/internal_api/public/util/unrecoverable_error_handler.h"
43 #include "sync/internal_api/public/util/weak_handle.h"
44 #include "sync/js/js_event_details.h"
45 #include "sync/js/js_event_handler.h"
46 #include "sync/test/fake_encryptor.h"
47 #include "sync/tools/invalidation_helper.h"
48 #include "sync/tools/null_invalidation_state_tracker.h"
51 #if defined(OS_MACOSX)
52 #include "base/mac/scoped_nsautorelease_pool.h"
55 // This is a simple utility that initializes a sync client and
56 // prints out any events.
58 // TODO(akalin): Refactor to combine shared code with
59 // sync_listen_notifications.
63 const char kEmailSwitch
[] = "email";
64 const char kTokenSwitch
[] = "token";
65 const char kXmppHostPortSwitch
[] = "xmpp-host-port";
66 const char kXmppTrySslTcpFirstSwitch
[] = "xmpp-try-ssltcp-first";
67 const char kXmppAllowInsecureConnectionSwitch
[] =
68 "xmpp-allow-insecure-connection";
69 const char kSyncServiceURL
[] = "https://clients4.google.com/chrome-sync/dev";
71 // Needed to use a real host resolver.
72 class MyTestURLRequestContext
: public net::TestURLRequestContext
{
74 MyTestURLRequestContext() : TestURLRequestContext(true) {
75 context_storage_
.set_host_resolver(
76 net::HostResolver::CreateDefaultResolver(NULL
));
77 context_storage_
.set_transport_security_state(
78 new net::TransportSecurityState());
82 ~MyTestURLRequestContext() override
{}
85 class MyTestURLRequestContextGetter
: public net::TestURLRequestContextGetter
{
87 explicit MyTestURLRequestContextGetter(
88 const scoped_refptr
<base::SingleThreadTaskRunner
>& io_task_runner
)
89 : TestURLRequestContextGetter(io_task_runner
) {}
91 net::TestURLRequestContext
* GetURLRequestContext() override
{
92 // Construct |context_| lazily so it gets constructed on the right
93 // thread (the IO thread).
95 context_
.reset(new MyTestURLRequestContext());
96 return context_
.get();
100 ~MyTestURLRequestContextGetter() override
{}
102 scoped_ptr
<MyTestURLRequestContext
> context_
;
105 // TODO(akalin): Use system encryptor once it's moved to sync/.
106 class NullEncryptor
: public Encryptor
{
108 ~NullEncryptor() override
{}
110 bool EncryptString(const std::string
& plaintext
,
111 std::string
* ciphertext
) override
{
112 *ciphertext
= plaintext
;
116 bool DecryptString(const std::string
& ciphertext
,
117 std::string
* plaintext
) override
{
118 *plaintext
= ciphertext
;
123 std::string
ValueToString(const base::Value
& value
) {
125 base::JSONWriter::Write(&value
, &str
);
129 class LoggingChangeDelegate
: public SyncManager::ChangeDelegate
{
131 ~LoggingChangeDelegate() override
{}
133 void OnChangesApplied(ModelType model_type
,
135 const BaseTransaction
* trans
,
136 const ImmutableChangeRecordList
& changes
) override
{
137 LOG(INFO
) << "Changes applied for "
138 << ModelTypeToString(model_type
);
140 size_t change_count
= changes
.Get().size();
141 for (ChangeRecordList::const_iterator it
=
142 changes
.Get().begin(); it
!= changes
.Get().end(); ++it
) {
143 scoped_ptr
<base::DictionaryValue
> change_value(it
->ToValue());
144 LOG(INFO
) << "Change (" << i
<< "/" << change_count
<< "): "
145 << ValueToString(*change_value
);
146 if (it
->action
!= ChangeRecord::ACTION_DELETE
) {
147 ReadNode
node(trans
);
148 CHECK_EQ(node
.InitByIdLookup(it
->id
), BaseNode::INIT_OK
);
149 scoped_ptr
<base::DictionaryValue
> details(node
.ToValue());
150 VLOG(1) << "Details: " << ValueToString(*details
);
156 void OnChangesComplete(ModelType model_type
) override
{
157 LOG(INFO
) << "Changes complete for "
158 << ModelTypeToString(model_type
);
162 class LoggingUnrecoverableErrorHandler
163 : public UnrecoverableErrorHandler
{
165 ~LoggingUnrecoverableErrorHandler() override
{}
167 void OnUnrecoverableError(const tracked_objects::Location
& from_here
,
168 const std::string
& message
) override
{
169 if (LOG_IS_ON(ERROR
)) {
170 logging::LogMessage(from_here
.file_name(), from_here
.line_number(),
171 logging::LOG_ERROR
).stream()
177 class LoggingJsEventHandler
178 : public JsEventHandler
,
179 public base::SupportsWeakPtr
<LoggingJsEventHandler
> {
181 ~LoggingJsEventHandler() override
{}
183 void HandleJsEvent(const std::string
& name
,
184 const JsEventDetails
& details
) override
{
185 VLOG(1) << name
<< ": " << details
.ToString();
189 class InvalidationAdapter
: public syncer::InvalidationInterface
{
191 explicit InvalidationAdapter(const syncer::Invalidation
& invalidation
)
192 : invalidation_(invalidation
) {}
193 ~InvalidationAdapter() override
{}
195 bool IsUnknownVersion() const override
{
196 return invalidation_
.is_unknown_version();
199 const std::string
& GetPayload() const override
{
200 return invalidation_
.payload();
203 int64
GetVersion() const override
{ return invalidation_
.version(); }
205 void Acknowledge() override
{ invalidation_
.Acknowledge(); }
207 void Drop() override
{ invalidation_
.Drop(); }
210 syncer::Invalidation invalidation_
;
213 class InvalidatorShim
: public InvalidationHandler
{
215 explicit InvalidatorShim(SyncManager
* sync_manager
)
216 : sync_manager_(sync_manager
) {}
218 void OnInvalidatorStateChange(InvalidatorState state
) override
{
219 sync_manager_
->SetInvalidatorEnabled(state
== INVALIDATIONS_ENABLED
);
222 void OnIncomingInvalidation(
223 const ObjectIdInvalidationMap
& invalidation_map
) override
{
224 syncer::ObjectIdSet ids
= invalidation_map
.GetObjectIds();
225 for (syncer::ObjectIdSet::const_iterator ids_it
= ids
.begin();
228 syncer::ModelType type
;
229 if (!NotificationTypeToRealModelType(ids_it
->name(), &type
)) {
230 DLOG(WARNING
) << "Notification has invalid id: "
231 << syncer::ObjectIdToString(*ids_it
);
233 syncer::SingleObjectInvalidationSet invalidation_set
=
234 invalidation_map
.ForObject(*ids_it
);
235 for (syncer::SingleObjectInvalidationSet::const_iterator inv_it
=
236 invalidation_set
.begin();
237 inv_it
!= invalidation_set
.end();
239 scoped_ptr
<syncer::InvalidationInterface
> inv_adapter(
240 new InvalidationAdapter(*inv_it
));
241 sync_manager_
->OnIncomingInvalidation(type
, inv_adapter
.Pass());
247 std::string
GetOwnerName() const override
{ return "InvalidatorShim"; }
250 SyncManager
* sync_manager_
;
253 void LogUnrecoverableErrorContext() {
254 base::debug::StackTrace().Print();
257 notifier::NotifierOptions
ParseNotifierOptions(
258 const base::CommandLine
& command_line
,
259 const scoped_refptr
<net::URLRequestContextGetter
>& request_context_getter
) {
260 notifier::NotifierOptions notifier_options
;
261 notifier_options
.request_context_getter
= request_context_getter
;
262 notifier_options
.auth_mechanism
= "X-OAUTH2";
264 if (command_line
.HasSwitch(kXmppHostPortSwitch
)) {
265 notifier_options
.xmpp_host_port
=
266 net::HostPortPair::FromString(
267 command_line
.GetSwitchValueASCII(kXmppHostPortSwitch
));
268 LOG(INFO
) << "Using " << notifier_options
.xmpp_host_port
.ToString()
269 << " for test sync notification server.";
272 notifier_options
.try_ssltcp_first
=
273 command_line
.HasSwitch(kXmppTrySslTcpFirstSwitch
);
274 LOG_IF(INFO
, notifier_options
.try_ssltcp_first
)
275 << "Trying SSL/TCP port before XMPP port for notifications.";
277 notifier_options
.allow_insecure_connection
=
278 command_line
.HasSwitch(kXmppAllowInsecureConnectionSwitch
);
279 LOG_IF(INFO
, notifier_options
.allow_insecure_connection
)
280 << "Allowing insecure XMPP connections.";
282 return notifier_options
;
285 void StubNetworkTimeUpdateCallback(const base::Time
&,
286 const base::TimeDelta
&,
287 const base::TimeDelta
&) {
290 int SyncClientMain(int argc
, char* argv
[]) {
291 #if defined(OS_MACOSX)
292 base::mac::ScopedNSAutoreleasePool pool
;
294 base::AtExitManager exit_manager
;
295 base::CommandLine::Init(argc
, argv
);
296 logging::LoggingSettings settings
;
297 settings
.logging_dest
= logging::LOG_TO_SYSTEM_DEBUG_LOG
;
298 logging::InitLogging(settings
);
300 base::MessageLoop sync_loop
;
301 base::Thread
io_thread("IO thread");
302 base::Thread::Options options
;
303 options
.message_loop_type
= base::MessageLoop::TYPE_IO
;
304 io_thread
.StartWithOptions(options
);
306 // Parse command line.
307 const base::CommandLine
& command_line
=
308 *base::CommandLine::ForCurrentProcess();
309 SyncCredentials credentials
;
310 credentials
.email
= command_line
.GetSwitchValueASCII(kEmailSwitch
);
311 credentials
.sync_token
= command_line
.GetSwitchValueASCII(kTokenSwitch
);
312 // TODO(akalin): Write a wrapper script that gets a token for an
313 // email and password and passes that in to this utility.
314 if (credentials
.email
.empty() || credentials
.sync_token
.empty()) {
315 std::printf("Usage: %s --%s=foo@bar.com --%s=token\n"
316 "[--%s=host:port] [--%s] [--%s]\n"
317 "Run chrome and set a breakpoint on\n"
318 "syncer::SyncManagerImpl::UpdateCredentials() "
319 "after logging into\n"
320 "sync to get the token to pass into this utility.\n",
322 kEmailSwitch
, kTokenSwitch
, kXmppHostPortSwitch
,
323 kXmppTrySslTcpFirstSwitch
,
324 kXmppAllowInsecureConnectionSwitch
);
328 // Set up objects that monitor the network.
329 scoped_ptr
<net::NetworkChangeNotifier
> network_change_notifier(
330 net::NetworkChangeNotifier::Create());
332 // Set up sync notifier factory.
333 const scoped_refptr
<MyTestURLRequestContextGetter
> context_getter
=
334 new MyTestURLRequestContextGetter(io_thread
.message_loop_proxy());
335 const notifier::NotifierOptions
& notifier_options
=
336 ParseNotifierOptions(command_line
, context_getter
);
337 syncer::NetworkChannelCreator network_channel_creator
=
338 syncer::NonBlockingInvalidator::MakePushClientChannelCreator(
340 const char kClientInfo
[] = "standalone_sync_client";
341 std::string invalidator_id
= base::RandBytesAsString(8);
342 NullInvalidationStateTracker null_invalidation_state_tracker
;
343 scoped_ptr
<Invalidator
> invalidator(new NonBlockingInvalidator(
344 network_channel_creator
,
346 null_invalidation_state_tracker
.GetSavedInvalidations(),
347 null_invalidation_state_tracker
.GetBootstrapData(),
348 &null_invalidation_state_tracker
,
350 notifier_options
.request_context_getter
));
352 // Set up database directory for the syncer.
353 base::ScopedTempDir database_dir
;
354 CHECK(database_dir
.CreateUniqueTempDir());
356 // Developers often add types to ModelTypeSet::All() before the server
357 // supports them. We need to be explicit about which types we want here.
358 ModelTypeSet model_types
;
359 model_types
.Put(BOOKMARKS
);
360 model_types
.Put(PREFERENCES
);
361 model_types
.Put(PASSWORDS
);
362 model_types
.Put(AUTOFILL
);
363 model_types
.Put(THEMES
);
364 model_types
.Put(TYPED_URLS
);
365 model_types
.Put(EXTENSIONS
);
366 model_types
.Put(NIGORI
);
367 model_types
.Put(SEARCH_ENGINES
);
368 model_types
.Put(SESSIONS
);
369 model_types
.Put(APPS
);
370 model_types
.Put(AUTOFILL_PROFILE
);
371 model_types
.Put(APP_SETTINGS
);
372 model_types
.Put(EXTENSION_SETTINGS
);
373 model_types
.Put(APP_NOTIFICATIONS
);
374 model_types
.Put(HISTORY_DELETE_DIRECTIVES
);
375 model_types
.Put(SYNCED_NOTIFICATIONS
);
376 model_types
.Put(SYNCED_NOTIFICATION_APP_INFO
);
377 model_types
.Put(DEVICE_INFO
);
378 model_types
.Put(EXPERIMENTS
);
379 model_types
.Put(PRIORITY_PREFERENCES
);
380 model_types
.Put(DICTIONARY
);
381 model_types
.Put(FAVICON_IMAGES
);
382 model_types
.Put(FAVICON_TRACKING
);
384 ModelSafeRoutingInfo routing_info
;
385 for (ModelTypeSet::Iterator it
= model_types
.First();
386 it
.Good(); it
.Inc()) {
387 routing_info
[it
.Get()] = GROUP_PASSIVE
;
389 scoped_refptr
<PassiveModelWorker
> passive_model_safe_worker
=
390 new PassiveModelWorker(&sync_loop
, NULL
);
391 std::vector
<scoped_refptr
<ModelSafeWorker
> > workers
;
392 workers
.push_back(passive_model_safe_worker
);
394 // Set up sync manager.
395 SyncManagerFactory
sync_manager_factory(SyncManagerFactory::NORMAL
);
396 scoped_ptr
<SyncManager
> sync_manager
=
397 sync_manager_factory
.CreateSyncManager("sync_client manager");
398 LoggingJsEventHandler js_event_handler
;
399 // Used only by InitialProcessMetadata(), so it's okay to leave this as NULL.
400 const scoped_refptr
<base::TaskRunner
> blocking_task_runner
= NULL
;
401 const char kUserAgent
[] = "sync_client";
402 // TODO(akalin): Replace this with just the context getter once
403 // HttpPostProviderFactory is removed.
404 CancelationSignal factory_cancelation_signal
;
405 scoped_ptr
<HttpPostProviderFactory
> post_factory(
406 new HttpBridgeFactory(context_getter
.get(),
407 base::Bind(&StubNetworkTimeUpdateCallback
),
408 &factory_cancelation_signal
));
409 post_factory
->Init(kUserAgent
);
410 // Used only when committing bookmarks, so it's okay to leave this
412 ExtensionsActivity
* extensions_activity
= NULL
;
413 LoggingChangeDelegate change_delegate
;
414 const char kRestoredKeyForBootstrapping
[] = "";
415 const char kRestoredKeystoreKeyForBootstrapping
[] = "";
416 NullEncryptor null_encryptor
;
417 InternalComponentsFactoryImpl::Switches factory_switches
= {
418 InternalComponentsFactory::ENCRYPTION_KEYSTORE
,
419 InternalComponentsFactory::BACKOFF_NORMAL
421 CancelationSignal scm_cancelation_signal
;
423 SyncManager::InitArgs args
;
424 args
.database_location
= database_dir
.path();
425 args
.event_handler
= WeakHandle
<JsEventHandler
>(js_event_handler
.AsWeakPtr());
426 args
.service_url
= GURL(kSyncServiceURL
);
427 args
.post_factory
= post_factory
.Pass();
428 args
.workers
= workers
;
429 args
.extensions_activity
= extensions_activity
;
430 args
.change_delegate
= &change_delegate
;
431 args
.credentials
= credentials
;
432 args
.invalidator_client_id
= invalidator_id
;
433 args
.restored_key_for_bootstrapping
= kRestoredKeyForBootstrapping
;
434 args
.restored_keystore_key_for_bootstrapping
=
435 kRestoredKeystoreKeyForBootstrapping
;
436 args
.internal_components_factory
.reset(
437 new InternalComponentsFactoryImpl(factory_switches
));
438 args
.encryptor
= &null_encryptor
;
439 args
.unrecoverable_error_handler
.reset(new LoggingUnrecoverableErrorHandler
);
440 args
.report_unrecoverable_error_function
= &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 invalidator
->UpdateRegisteredIds(
449 shim
.get(), ModelTypeSetToObjectIdSet(model_types
));
450 sync_manager
->StartSyncingNormally(routing_info
);
459 } // namespace syncer
461 int main(int argc
, char* argv
[]) {
462 return syncer::SyncClientMain(argc
, argv
);