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 <cstddef>
6#include <cstdio>
7#include <string>
8
9#include "base/at_exit.h"
10#include "base/command_line.h"
11#include "base/compiler_specific.h"
12#include "base/logging.h"
13#include "base/memory/ref_counted.h"
14#include "base/memory/scoped_ptr.h"
15#include "base/message_loop/message_loop.h"
16#include "base/rand_util.h"
17#include "base/threading/thread.h"
18#include "components/invalidation/invalidation_handler.h"
19#include "components/invalidation/invalidation_state_tracker.h"
20#include "components/invalidation/invalidation_util.h"
21#include "components/invalidation/invalidator.h"
22#include "components/invalidation/non_blocking_invalidator.h"
23#include "components/invalidation/object_id_invalidation_map.h"
24#include "jingle/notifier/base/notification_method.h"
25#include "jingle/notifier/base/notifier_options.h"
26#include "net/base/host_port_pair.h"
27#include "net/base/network_change_notifier.h"
28#include "net/dns/host_resolver.h"
29#include "net/http/transport_security_state.h"
30#include "net/url_request/url_request_test_util.h"
31#include "sync/internal_api/public/base/model_type.h"
32#include "sync/tools/invalidation_helper.h"
33#include "sync/tools/null_invalidation_state_tracker.h"
34
35#if defined(OS_MACOSX)
36#include "base/mac/scoped_nsautorelease_pool.h"
37#endif
38
39// This is a simple utility that initializes a sync notifier and
40// listens to any received notifications.
41
42namespace syncer {
43namespace {
44
45const char kEmailSwitch[] = "email";
46const char kTokenSwitch[] = "token";
47const char kHostPortSwitch[] = "host-port";
48const char kTrySslTcpFirstSwitch[] = "try-ssltcp-first";
49const char kAllowInsecureConnectionSwitch[] = "allow-insecure-connection";
50
51// Class to print received notifications events.
52class NotificationPrinter : public InvalidationHandler {
53 public:
54  NotificationPrinter() {}
55  virtual ~NotificationPrinter() {}
56
57  virtual void OnInvalidatorStateChange(InvalidatorState state) OVERRIDE {
58    LOG(INFO) << "Invalidator state changed to "
59              << InvalidatorStateToString(state);
60  }
61
62  virtual void OnIncomingInvalidation(
63      const ObjectIdInvalidationMap& invalidation_map) OVERRIDE {
64    ObjectIdSet ids = invalidation_map.GetObjectIds();
65    for (ObjectIdSet::const_iterator it = ids.begin(); it != ids.end(); ++it) {
66      LOG(INFO) << "Remote invalidation: "
67                << invalidation_map.ToString();
68    }
69  }
70
71  virtual std::string GetOwnerName() const OVERRIDE {
72    return "NotificationPrinter";
73  }
74
75 private:
76  DISALLOW_COPY_AND_ASSIGN(NotificationPrinter);
77};
78
79// Needed to use a real host resolver.
80class MyTestURLRequestContext : public net::TestURLRequestContext {
81 public:
82  MyTestURLRequestContext() : TestURLRequestContext(true) {
83    context_storage_.set_host_resolver(
84        net::HostResolver::CreateDefaultResolver(NULL));
85    context_storage_.set_transport_security_state(
86        new net::TransportSecurityState());
87    Init();
88  }
89
90  virtual ~MyTestURLRequestContext() {}
91};
92
93class MyTestURLRequestContextGetter : public net::TestURLRequestContextGetter {
94 public:
95  explicit MyTestURLRequestContextGetter(
96      const scoped_refptr<base::SingleThreadTaskRunner>& io_task_runner)
97      : TestURLRequestContextGetter(io_task_runner) {}
98
99  virtual net::TestURLRequestContext* GetURLRequestContext() OVERRIDE {
100    // Construct |context_| lazily so it gets constructed on the right
101    // thread (the IO thread).
102    if (!context_)
103      context_.reset(new MyTestURLRequestContext());
104    return context_.get();
105  }
106
107 private:
108  virtual ~MyTestURLRequestContextGetter() {}
109
110  scoped_ptr<MyTestURLRequestContext> context_;
111};
112
113notifier::NotifierOptions ParseNotifierOptions(
114    const CommandLine& command_line,
115    const scoped_refptr<net::URLRequestContextGetter>&
116        request_context_getter) {
117  notifier::NotifierOptions notifier_options;
118  notifier_options.request_context_getter = request_context_getter;
119
120  if (command_line.HasSwitch(kHostPortSwitch)) {
121    notifier_options.xmpp_host_port =
122        net::HostPortPair::FromString(
123            command_line.GetSwitchValueASCII(kHostPortSwitch));
124    LOG(INFO) << "Using " << notifier_options.xmpp_host_port.ToString()
125              << " for test sync notification server.";
126  }
127
128  notifier_options.try_ssltcp_first =
129      command_line.HasSwitch(kTrySslTcpFirstSwitch);
130  LOG_IF(INFO, notifier_options.try_ssltcp_first)
131      << "Trying SSL/TCP port before XMPP port for notifications.";
132
133  notifier_options.allow_insecure_connection =
134      command_line.HasSwitch(kAllowInsecureConnectionSwitch);
135  LOG_IF(INFO, notifier_options.allow_insecure_connection)
136      << "Allowing insecure XMPP connections.";
137
138  return notifier_options;
139}
140
141int SyncListenNotificationsMain(int argc, char* argv[]) {
142  using namespace syncer;
143#if defined(OS_MACOSX)
144  base::mac::ScopedNSAutoreleasePool pool;
145#endif
146  base::AtExitManager exit_manager;
147  CommandLine::Init(argc, argv);
148  logging::LoggingSettings settings;
149  settings.logging_dest = logging::LOG_TO_SYSTEM_DEBUG_LOG;
150  logging::InitLogging(settings);
151
152  base::MessageLoop ui_loop;
153  base::Thread io_thread("IO thread");
154  base::Thread::Options options;
155  options.message_loop_type = base::MessageLoop::TYPE_IO;
156  io_thread.StartWithOptions(options);
157
158  // Parse command line.
159  const CommandLine& command_line = *CommandLine::ForCurrentProcess();
160  std::string email = command_line.GetSwitchValueASCII(kEmailSwitch);
161  std::string token = command_line.GetSwitchValueASCII(kTokenSwitch);
162  // TODO(akalin): Write a wrapper script that gets a token for an
163  // email and password and passes that in to this utility.
164  if (email.empty() || token.empty()) {
165    std::printf("Usage: %s --%s=foo@bar.com --%s=token\n"
166                "[--%s=host:port] [--%s] [--%s]\n"
167                "Run chrome and set a breakpoint on\n"
168                "syncer::SyncManagerImpl::UpdateCredentials() "
169                "after logging into\n"
170                "sync to get the token to pass into this utility.\n",
171                argv[0],
172                kEmailSwitch, kTokenSwitch, kHostPortSwitch,
173                kTrySslTcpFirstSwitch, kAllowInsecureConnectionSwitch);
174    return -1;
175  }
176
177  // Set up objects that monitor the network.
178  scoped_ptr<net::NetworkChangeNotifier> network_change_notifier(
179      net::NetworkChangeNotifier::Create());
180
181  const notifier::NotifierOptions& notifier_options =
182      ParseNotifierOptions(
183          command_line,
184          new MyTestURLRequestContextGetter(io_thread.message_loop_proxy()));
185  syncer::NetworkChannelCreator network_channel_creator =
186      syncer::NonBlockingInvalidator::MakePushClientChannelCreator(
187          notifier_options);
188  const char kClientInfo[] = "sync_listen_notifications";
189  NullInvalidationStateTracker null_invalidation_state_tracker;
190  scoped_ptr<Invalidator> invalidator(
191      new NonBlockingInvalidator(
192          network_channel_creator,
193          base::RandBytesAsString(8),
194          null_invalidation_state_tracker.GetSavedInvalidations(),
195          null_invalidation_state_tracker.GetBootstrapData(),
196          &null_invalidation_state_tracker,
197          kClientInfo,
198          notifier_options.request_context_getter));
199
200  NotificationPrinter notification_printer;
201
202  invalidator->UpdateCredentials(email, token);
203
204  // Listen for notifications for all known types.
205  invalidator->RegisterHandler(&notification_printer);
206  invalidator->UpdateRegisteredIds(
207      &notification_printer, ModelTypeSetToObjectIdSet(ModelTypeSet::All()));
208
209  ui_loop.Run();
210
211  invalidator->UnregisterHandler(&notification_printer);
212  io_thread.Stop();
213  return 0;
214}
215
216}  // namespace
217}  // namespace syncer
218
219int main(int argc, char* argv[]) {
220  return syncer::SyncListenNotificationsMain(argc, argv);
221}
222