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 "jingle/notifier/base/notification_method.h"
24 #include "jingle/notifier/base/notifier_options.h"
25 #include "net/base/host_port_pair.h"
26 #include "net/base/network_change_notifier.h"
27 #include "net/dns/host_resolver.h"
28 #include "net/http/transport_security_state.h"
29 #include "net/url_request/url_request_test_util.h"
30 #include "sync/internal_api/public/base/cancelation_signal.h"
31 #include "sync/internal_api/public/base/model_type.h"
32 #include "sync/internal_api/public/base_node.h"
33 #include "sync/internal_api/public/engine/passive_model_worker.h"
34 #include "sync/internal_api/public/http_bridge.h"
35 #include "sync/internal_api/public/internal_components_factory_impl.h"
36 #include "sync/internal_api/public/read_node.h"
37 #include "sync/internal_api/public/sync_manager.h"
38 #include "sync/internal_api/public/sync_manager_factory.h"
39 #include "sync/internal_api/public/util/report_unrecoverable_error_function.h"
40 #include "sync/internal_api/public/util/unrecoverable_error_handler.h"
41 #include "sync/internal_api/public/util/weak_handle.h"
42 #include "sync/js/js_event_details.h"
43 #include "sync/js/js_event_handler.h"
44 #include "sync/notifier/non_blocking_invalidator.h"
45 #include "sync/test/fake_encryptor.h"
46 #include "sync/tools/null_invalidation_state_tracker.h"
48 #if defined(OS_MACOSX)
49 #include "base/mac/scoped_nsautorelease_pool.h"
52 // This is a simple utility that initializes a sync client and
53 // prints out any events.
55 // TODO(akalin): Refactor to combine shared code with
56 // sync_listen_notifications.
60 const char kEmailSwitch
[] = "email";
61 const char kTokenSwitch
[] = "token";
62 const char kXmppHostPortSwitch
[] = "xmpp-host-port";
63 const char kXmppTrySslTcpFirstSwitch
[] = "xmpp-try-ssltcp-first";
64 const char kXmppAllowInsecureConnectionSwitch
[] =
65 "xmpp-allow-insecure-connection";
67 // Needed to use a real host resolver.
68 class MyTestURLRequestContext
: public net::TestURLRequestContext
{
70 MyTestURLRequestContext() : TestURLRequestContext(true) {
71 context_storage_
.set_host_resolver(
72 net::HostResolver::CreateDefaultResolver(NULL
));
73 context_storage_
.set_transport_security_state(
74 new net::TransportSecurityState());
78 virtual ~MyTestURLRequestContext() {}
81 class MyTestURLRequestContextGetter
: public net::TestURLRequestContextGetter
{
83 explicit MyTestURLRequestContextGetter(
84 const scoped_refptr
<base::MessageLoopProxy
>& io_message_loop_proxy
)
85 : TestURLRequestContextGetter(io_message_loop_proxy
) {}
87 virtual net::TestURLRequestContext
* GetURLRequestContext() OVERRIDE
{
88 // Construct |context_| lazily so it gets constructed on the right
89 // thread (the IO thread).
91 context_
.reset(new MyTestURLRequestContext());
92 return context_
.get();
96 virtual ~MyTestURLRequestContextGetter() {}
98 scoped_ptr
<MyTestURLRequestContext
> context_
;
101 // TODO(akalin): Use system encryptor once it's moved to sync/.
102 class NullEncryptor
: public Encryptor
{
104 virtual ~NullEncryptor() {}
106 virtual bool EncryptString(const std::string
& plaintext
,
107 std::string
* ciphertext
) OVERRIDE
{
108 *ciphertext
= plaintext
;
112 virtual bool DecryptString(const std::string
& ciphertext
,
113 std::string
* plaintext
) OVERRIDE
{
114 *plaintext
= ciphertext
;
119 std::string
ValueToString(const base::Value
& value
) {
121 base::JSONWriter::Write(&value
, &str
);
125 class LoggingChangeDelegate
: public SyncManager::ChangeDelegate
{
127 virtual ~LoggingChangeDelegate() {}
129 virtual void OnChangesApplied(
130 ModelType model_type
,
132 const BaseTransaction
* trans
,
133 const ImmutableChangeRecordList
& changes
) OVERRIDE
{
134 LOG(INFO
) << "Changes applied for "
135 << ModelTypeToString(model_type
);
137 size_t change_count
= changes
.Get().size();
138 for (ChangeRecordList::const_iterator it
=
139 changes
.Get().begin(); it
!= changes
.Get().end(); ++it
) {
140 scoped_ptr
<base::DictionaryValue
> change_value(it
->ToValue());
141 LOG(INFO
) << "Change (" << i
<< "/" << change_count
<< "): "
142 << ValueToString(*change_value
);
143 if (it
->action
!= ChangeRecord::ACTION_DELETE
) {
144 ReadNode
node(trans
);
145 CHECK_EQ(node
.InitByIdLookup(it
->id
), BaseNode::INIT_OK
);
146 scoped_ptr
<base::DictionaryValue
> details(node
.ToValue());
147 VLOG(1) << "Details: " << ValueToString(*details
);
153 virtual void OnChangesComplete(ModelType model_type
) OVERRIDE
{
154 LOG(INFO
) << "Changes complete for "
155 << ModelTypeToString(model_type
);
159 class LoggingUnrecoverableErrorHandler
160 : public UnrecoverableErrorHandler
{
162 virtual ~LoggingUnrecoverableErrorHandler() {}
164 virtual void OnUnrecoverableError(const tracked_objects::Location
& from_here
,
165 const std::string
& message
) OVERRIDE
{
166 if (LOG_IS_ON(ERROR
)) {
167 logging::LogMessage(from_here
.file_name(), from_here
.line_number(),
168 logging::LOG_ERROR
).stream()
174 class LoggingJsEventHandler
175 : public JsEventHandler
,
176 public base::SupportsWeakPtr
<LoggingJsEventHandler
> {
178 virtual ~LoggingJsEventHandler() {}
180 virtual void HandleJsEvent(
181 const std::string
& name
,
182 const JsEventDetails
& details
) OVERRIDE
{
183 VLOG(1) << name
<< ": " << details
.ToString();
187 void LogUnrecoverableErrorContext() {
188 base::debug::StackTrace().Print();
191 notifier::NotifierOptions
ParseNotifierOptions(
192 const CommandLine
& command_line
,
193 const scoped_refptr
<net::URLRequestContextGetter
>&
194 request_context_getter
) {
195 notifier::NotifierOptions notifier_options
;
196 notifier_options
.request_context_getter
= request_context_getter
;
197 notifier_options
.auth_mechanism
= "X-OAUTH2";
199 if (command_line
.HasSwitch(kXmppHostPortSwitch
)) {
200 notifier_options
.xmpp_host_port
=
201 net::HostPortPair::FromString(
202 command_line
.GetSwitchValueASCII(kXmppHostPortSwitch
));
203 LOG(INFO
) << "Using " << notifier_options
.xmpp_host_port
.ToString()
204 << " for test sync notification server.";
207 notifier_options
.try_ssltcp_first
=
208 command_line
.HasSwitch(kXmppTrySslTcpFirstSwitch
);
209 LOG_IF(INFO
, notifier_options
.try_ssltcp_first
)
210 << "Trying SSL/TCP port before XMPP port for notifications.";
212 notifier_options
.allow_insecure_connection
=
213 command_line
.HasSwitch(kXmppAllowInsecureConnectionSwitch
);
214 LOG_IF(INFO
, notifier_options
.allow_insecure_connection
)
215 << "Allowing insecure XMPP connections.";
217 return notifier_options
;
220 void StubNetworkTimeUpdateCallback(const base::Time
&,
221 const base::TimeDelta
&,
222 const base::TimeDelta
&) {
225 int SyncClientMain(int argc
, char* argv
[]) {
226 #if defined(OS_MACOSX)
227 base::mac::ScopedNSAutoreleasePool pool
;
229 base::AtExitManager exit_manager
;
230 CommandLine::Init(argc
, argv
);
231 logging::LoggingSettings settings
;
232 settings
.logging_dest
= logging::LOG_TO_SYSTEM_DEBUG_LOG
;
233 logging::InitLogging(settings
);
235 base::MessageLoop sync_loop
;
236 base::Thread
io_thread("IO thread");
237 base::Thread::Options options
;
238 options
.message_loop_type
= base::MessageLoop::TYPE_IO
;
239 io_thread
.StartWithOptions(options
);
241 // Parse command line.
242 const CommandLine
& command_line
= *CommandLine::ForCurrentProcess();
243 SyncCredentials credentials
;
244 credentials
.email
= command_line
.GetSwitchValueASCII(kEmailSwitch
);
245 credentials
.sync_token
= command_line
.GetSwitchValueASCII(kTokenSwitch
);
246 // TODO(akalin): Write a wrapper script that gets a token for an
247 // email and password and passes that in to this utility.
248 if (credentials
.email
.empty() || credentials
.sync_token
.empty()) {
249 std::printf("Usage: %s --%s=foo@bar.com --%s=token\n"
250 "[--%s=host:port] [--%s] [--%s]\n"
251 "Run chrome and set a breakpoint on\n"
252 "syncer::SyncManagerImpl::UpdateCredentials() "
253 "after logging into\n"
254 "sync to get the token to pass into this utility.\n",
256 kEmailSwitch
, kTokenSwitch
, kXmppHostPortSwitch
,
257 kXmppTrySslTcpFirstSwitch
,
258 kXmppAllowInsecureConnectionSwitch
);
262 // Set up objects that monitor the network.
263 scoped_ptr
<net::NetworkChangeNotifier
> network_change_notifier(
264 net::NetworkChangeNotifier::Create());
266 // Set up sync notifier factory.
267 const scoped_refptr
<MyTestURLRequestContextGetter
> context_getter
=
268 new MyTestURLRequestContextGetter(io_thread
.message_loop_proxy());
269 const notifier::NotifierOptions
& notifier_options
=
270 ParseNotifierOptions(command_line
, context_getter
);
271 syncer::NetworkChannelCreator network_channel_creator
=
272 syncer::NonBlockingInvalidator::MakePushClientChannelCreator(
274 const char kClientInfo
[] = "standalone_sync_client";
275 std::string invalidator_id
= base::RandBytesAsString(8);
276 NullInvalidationStateTracker null_invalidation_state_tracker
;
277 scoped_ptr
<Invalidator
> invalidator(new NonBlockingInvalidator(
278 network_channel_creator
,
280 null_invalidation_state_tracker
.GetSavedInvalidations(),
281 null_invalidation_state_tracker
.GetBootstrapData(),
282 &null_invalidation_state_tracker
,
284 notifier_options
.request_context_getter
));
286 // Set up database directory for the syncer.
287 base::ScopedTempDir database_dir
;
288 CHECK(database_dir
.CreateUniqueTempDir());
290 // Developers often add types to ModelTypeSet::All() before the server
291 // supports them. We need to be explicit about which types we want here.
292 ModelTypeSet model_types
;
293 model_types
.Put(BOOKMARKS
);
294 model_types
.Put(PREFERENCES
);
295 model_types
.Put(PASSWORDS
);
296 model_types
.Put(AUTOFILL
);
297 model_types
.Put(THEMES
);
298 model_types
.Put(TYPED_URLS
);
299 model_types
.Put(EXTENSIONS
);
300 model_types
.Put(NIGORI
);
301 model_types
.Put(SEARCH_ENGINES
);
302 model_types
.Put(SESSIONS
);
303 model_types
.Put(APPS
);
304 model_types
.Put(AUTOFILL_PROFILE
);
305 model_types
.Put(APP_SETTINGS
);
306 model_types
.Put(EXTENSION_SETTINGS
);
307 model_types
.Put(APP_NOTIFICATIONS
);
308 model_types
.Put(HISTORY_DELETE_DIRECTIVES
);
309 model_types
.Put(SYNCED_NOTIFICATIONS
);
310 model_types
.Put(SYNCED_NOTIFICATION_APP_INFO
);
311 model_types
.Put(DEVICE_INFO
);
312 model_types
.Put(EXPERIMENTS
);
313 model_types
.Put(PRIORITY_PREFERENCES
);
314 model_types
.Put(DICTIONARY
);
315 model_types
.Put(FAVICON_IMAGES
);
316 model_types
.Put(FAVICON_TRACKING
);
318 ModelSafeRoutingInfo routing_info
;
319 for (ModelTypeSet::Iterator it
= model_types
.First();
320 it
.Good(); it
.Inc()) {
321 routing_info
[it
.Get()] = GROUP_PASSIVE
;
323 scoped_refptr
<PassiveModelWorker
> passive_model_safe_worker
=
324 new PassiveModelWorker(&sync_loop
, NULL
);
325 std::vector
<scoped_refptr
<ModelSafeWorker
> > workers
;
326 workers
.push_back(passive_model_safe_worker
);
328 // Set up sync manager.
329 SyncManagerFactory sync_manager_factory
;
330 scoped_ptr
<SyncManager
> sync_manager
=
331 sync_manager_factory
.CreateSyncManager("sync_client manager");
332 LoggingJsEventHandler js_event_handler
;
333 const char kSyncServerAndPath
[] = "clients4.google.com/chrome-sync/dev";
334 int kSyncServerPort
= 443;
336 // Used only by InitialProcessMetadata(), so it's okay to leave this as NULL.
337 const scoped_refptr
<base::TaskRunner
> blocking_task_runner
= NULL
;
338 const char kUserAgent
[] = "sync_client";
339 // TODO(akalin): Replace this with just the context getter once
340 // HttpPostProviderFactory is removed.
341 CancelationSignal factory_cancelation_signal
;
342 scoped_ptr
<HttpPostProviderFactory
> post_factory(
343 new HttpBridgeFactory(context_getter
.get(),
344 base::Bind(&StubNetworkTimeUpdateCallback
),
345 &factory_cancelation_signal
));
346 post_factory
->Init(kUserAgent
);
347 // Used only when committing bookmarks, so it's okay to leave this
349 ExtensionsActivity
* extensions_activity
= NULL
;
350 LoggingChangeDelegate change_delegate
;
351 const char kRestoredKeyForBootstrapping
[] = "";
352 const char kRestoredKeystoreKeyForBootstrapping
[] = "";
353 NullEncryptor null_encryptor
;
354 InternalComponentsFactoryImpl::Switches factory_switches
= {
355 InternalComponentsFactory::ENCRYPTION_KEYSTORE
,
356 InternalComponentsFactory::BACKOFF_NORMAL
358 CancelationSignal scm_cancelation_signal
;
360 sync_manager
->Init(database_dir
.path(),
361 WeakHandle
<JsEventHandler
>(
362 js_event_handler
.AsWeakPtr()),
372 kRestoredKeyForBootstrapping
,
373 kRestoredKeystoreKeyForBootstrapping
,
374 new InternalComponentsFactoryImpl(factory_switches
),
376 scoped_ptr
<UnrecoverableErrorHandler
>(
377 new LoggingUnrecoverableErrorHandler
).Pass(),
378 &LogUnrecoverableErrorContext
,
379 &scm_cancelation_signal
);
380 // TODO(akalin): Avoid passing in model parameters multiple times by
381 // organizing handling of model types.
382 invalidator
->UpdateCredentials(credentials
.email
, credentials
.sync_token
);
383 invalidator
->RegisterHandler(sync_manager
.get());
384 invalidator
->UpdateRegisteredIds(
385 sync_manager
.get(), ModelTypeSetToObjectIdSet(model_types
));
386 sync_manager
->StartSyncingNormally(routing_info
);
395 } // namespace syncer
397 int main(int argc
, char* argv
[]) {
398 return syncer::SyncClientMain(argc
, argv
);