summaryrefslogtreecommitdiffstats
path: root/chrome/browser/sync/notifier/communicator
diff options
context:
space:
mode:
authornick@chromium.org <nick@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-09-10 06:05:27 +0000
committernick@chromium.org <nick@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-09-10 06:05:27 +0000
commit5852edc1b6eab234b9e048c41dd0d664ae7fc747 (patch)
tree9e5d8eb4833b76cdb11e66fc3607689e0f5e0122 /chrome/browser/sync/notifier/communicator
parentf6059e37f8b8ac335ce18a189a13e702974a1c7e (diff)
downloadchromium_src-5852edc1b6eab234b9e048c41dd0d664ae7fc747.zip
chromium_src-5852edc1b6eab234b9e048c41dd0d664ae7fc747.tar.gz
chromium_src-5852edc1b6eab234b9e048c41dd0d664ae7fc747.tar.bz2
Initial commit of sync engine code to browser/sync.
The code is not built on any platform yet. That will arrive as a subsequent checkin. This is an implementation of the interface exposed earlier through syncapi.h. It is the client side of a sync protocol that lets users sync their browser data (currently, just bookmarks) with their Google Account. Table of contents: browser/sync/ protocol - The protocol definition, and other definitions necessary to connect to the service. syncable/ - defines a data model for syncable objects, and provides a sqlite-based backing store for this model. engine/ - includes the core sync logic, including commiting changes to the server, downloading changes from the server, resolving conflicts, other parts of the sync algorithm. engine/net - parts of the sync engine focused on the business of talking to the server. Some of this is binds a generic "server connection" interface to a concrete implementation provided by Chromium. notifier - the part of the syncer focused on the business of sending and receiving xmpp notifications. Notifications are used instead of polling to achieve very low latency change propagation. util - not necessarily sync specific utility code. Much of this is scaffolding which should either be replaced by, or merged with, the utility code in base/. BUG=none TEST=this code includes its own suite of unit tests. Review URL: http://codereview.chromium.org/194065 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@25850 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/sync/notifier/communicator')
-rw-r--r--chrome/browser/sync/notifier/communicator/auth_task.cc69
-rw-r--r--chrome/browser/sync/notifier/communicator/auth_task.h77
-rw-r--r--chrome/browser/sync/notifier/communicator/auto_reconnect.cc155
-rw-r--r--chrome/browser/sync/notifier/communicator/auto_reconnect.h71
-rw-r--r--chrome/browser/sync/notifier/communicator/connection_options.cc16
-rw-r--r--chrome/browser/sync/notifier/communicator/connection_options.h55
-rw-r--r--chrome/browser/sync/notifier/communicator/connection_settings.cc126
-rw-r--r--chrome/browser/sync/notifier/communicator/connection_settings.h78
-rw-r--r--chrome/browser/sync/notifier/communicator/const_communicator.h11
-rw-r--r--chrome/browser/sync/notifier/communicator/login.cc361
-rw-r--r--chrome/browser/sync/notifier/communicator/login.h155
-rw-r--r--chrome/browser/sync/notifier/communicator/login_failure.cc45
-rw-r--r--chrome/browser/sync/notifier/communicator/login_failure.h69
-rw-r--r--chrome/browser/sync/notifier/communicator/login_settings.cc57
-rw-r--r--chrome/browser/sync/notifier/communicator/login_settings.h97
-rw-r--r--chrome/browser/sync/notifier/communicator/mailbox.cc682
-rw-r--r--chrome/browser/sync/notifier/communicator/mailbox.h166
-rw-r--r--chrome/browser/sync/notifier/communicator/mailbox_unittest.cc118
-rw-r--r--chrome/browser/sync/notifier/communicator/product_info.cc15
-rw-r--r--chrome/browser/sync/notifier/communicator/product_info.h14
-rw-r--r--chrome/browser/sync/notifier/communicator/single_login_attempt.cc562
-rw-r--r--chrome/browser/sync/notifier/communicator/single_login_attempt.h139
-rw-r--r--chrome/browser/sync/notifier/communicator/talk_auth_task.cc73
-rw-r--r--chrome/browser/sync/notifier/communicator/talk_auth_task.h62
-rw-r--r--chrome/browser/sync/notifier/communicator/xml_parse_helpers-inl.h24
-rw-r--r--chrome/browser/sync/notifier/communicator/xml_parse_helpers.cc185
-rw-r--r--chrome/browser/sync/notifier/communicator/xml_parse_helpers.h75
-rw-r--r--chrome/browser/sync/notifier/communicator/xmpp_connection_generator.cc210
-rw-r--r--chrome/browser/sync/notifier/communicator/xmpp_connection_generator.h81
-rw-r--r--chrome/browser/sync/notifier/communicator/xmpp_log.cc111
-rw-r--r--chrome/browser/sync/notifier/communicator/xmpp_log.h45
-rw-r--r--chrome/browser/sync/notifier/communicator/xmpp_socket_adapter.cc437
-rw-r--r--chrome/browser/sync/notifier/communicator/xmpp_socket_adapter.h85
33 files changed, 4526 insertions, 0 deletions
diff --git a/chrome/browser/sync/notifier/communicator/auth_task.cc b/chrome/browser/sync/notifier/communicator/auth_task.cc
new file mode 100644
index 0000000..11eba2d
--- /dev/null
+++ b/chrome/browser/sync/notifier/communicator/auth_task.cc
@@ -0,0 +1,69 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/sync/notifier/communicator/auth_task.h"
+
+#include "chrome/browser/sync/notifier/gaia_auth/gaiaauth.h"
+#include "chrome/browser/sync/notifier/communicator/login.h"
+#include "chrome/browser/sync/notifier/communicator/login_settings.h"
+#include "chrome/browser/sync/notifier/communicator/product_info.h"
+#include "talk/base/common.h"
+#include "talk/base/urlencode.h"
+#include "talk/xmpp/xmppclient.h"
+
+namespace notifier {
+const char kTalkGadgetAuthPath[] = "/auth";
+
+AuthTask::AuthTask(talk_base::Task* parent, Login* login, const char* url)
+ : talk_base::Task(parent),
+ login_(login),
+ url_(url),
+ use_gaia_redirect_(true) {
+ ASSERT(login && !url_.empty());
+}
+
+int AuthTask::ProcessStart() {
+ auth_.reset(new buzz::GaiaAuth(GetUserAgentString(),
+ GetProductSignature()));
+ auth_->SignalAuthDone.connect(this, &AuthTask::OnAuthDone);
+ auth_->StartTokenAuth(login_->xmpp_client()->jid().BareJid(),
+ login_->login_settings().user_settings().pass(),
+ use_gaia_redirect_ ? "gaia" : service_);
+ return STATE_RESPONSE;
+}
+
+int AuthTask::ProcessResponse() {
+ ASSERT(auth_.get());
+ if (!auth_->IsAuthDone()) {
+ return STATE_BLOCKED;
+ }
+ if (!auth_->IsAuthorized()) {
+ SignalAuthError(!auth_->HadError());
+ return STATE_ERROR;
+ }
+
+ std::string uber_url;
+ if (use_gaia_redirect_) {
+ uber_url = auth_->CreateAuthenticatedUrl(url_, service_);
+ } else {
+ uber_url = redir_auth_prefix_ + auth_->GetAuthCookie();
+ uber_url += redir_continue_;
+ uber_url += UrlEncodeString(url_);
+ }
+
+ if (uber_url == "") {
+ SignalAuthError(true);
+ return STATE_ERROR;
+ }
+
+ SignalAuthDone(uber_url);
+ return STATE_DONE;
+}
+
+
+void AuthTask::OnAuthDone() {
+ Wake();
+}
+
+} // namespace notifier
diff --git a/chrome/browser/sync/notifier/communicator/auth_task.h b/chrome/browser/sync/notifier/communicator/auth_task.h
new file mode 100644
index 0000000..b5141f8
--- /dev/null
+++ b/chrome/browser/sync/notifier/communicator/auth_task.h
@@ -0,0 +1,77 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_SYNC_NOTIFIER_COMMUNICATOR_AUTH_TASK_H_
+#define CHROME_BROWSER_SYNC_NOTIFIER_COMMUNICATOR_AUTH_TASK_H_
+
+#include <string>
+#include "talk/base/scoped_ptr.h"
+#include "talk/base/sigslot.h"
+#include "talk/base/task.h"
+
+namespace buzz {
+class GaiaAuth;
+}
+
+namespace notifier {
+class Login;
+
+// Create an authenticated talk url from an unauthenticated url
+class AuthTask : public talk_base::Task, public sigslot::has_slots<> {
+ public:
+ AuthTask(talk_base::Task* parent, Login* login, const char* url);
+
+ // An abort method which doesn't take any parameters.
+ // (talk_base::Task::Abort() has a default parameter.)
+ //
+ // The primary purpose of this method is to allow a
+ // signal to be hooked up to abort this task.
+ void Abort() {
+ talk_base::Task::Abort();
+ }
+
+ void set_service(const char* service) {
+ service_ = service;
+ }
+
+ void set_use_gaia_redirect(bool use_gaia_redirect) {
+ use_gaia_redirect_ = use_gaia_redirect;
+ }
+
+ void set_redir_auth_prefix(const char* redir_auth_prefix) {
+ redir_auth_prefix_ = redir_auth_prefix;
+ }
+
+ void set_redir_continue(const char* redir_continue) {
+ redir_continue_ = redir_continue;
+ }
+
+ sigslot::signal1<const std::string&> SignalAuthDone;
+ sigslot::signal1<bool> SignalAuthError;
+
+ protected:
+ virtual int ProcessStart();
+ virtual int ProcessResponse();
+
+ private:
+ void OnAuthDone();
+
+ scoped_ptr<buzz::GaiaAuth> auth_;
+ Login* login_;
+ std::string service_;
+ std::string url_;
+
+ // the following members are used for cases where we don't want to
+ // redirect through gaia, but rather via the end-site's mechanism
+ // (We need this for orkut)
+ bool use_gaia_redirect_;
+ std::string redir_auth_prefix_;
+ std::string redir_continue_;
+
+ DISALLOW_COPY_AND_ASSIGN(AuthTask);
+};
+
+} // namespace notifier
+
+#endif // CHROME_BROWSER_SYNC_NOTIFIER_COMMUNICATOR_AUTH_TASK_H_
diff --git a/chrome/browser/sync/notifier/communicator/auto_reconnect.cc b/chrome/browser/sync/notifier/communicator/auto_reconnect.cc
new file mode 100644
index 0000000..eadfe46
--- /dev/null
+++ b/chrome/browser/sync/notifier/communicator/auto_reconnect.cc
@@ -0,0 +1,155 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/sync/notifier/communicator/auto_reconnect.h"
+
+#include "chrome/browser/sync/notifier/base/network_status_detector_task.h"
+#include "chrome/browser/sync/notifier/base/time.h"
+#include "chrome/browser/sync/notifier/base/timer.h"
+#include "talk/base/common.h"
+
+namespace notifier {
+const int kResetReconnectInfoDelaySec = 2;
+
+AutoReconnect::AutoReconnect(talk_base::Task* parent,
+ NetworkStatusDetectorTask* network_status)
+ : reconnect_interval_ns_(0),
+ reconnect_timer_(NULL),
+ delayed_reset_timer_(NULL),
+ parent_(parent),
+ is_idle_(false) {
+ SetupReconnectInterval();
+ if (network_status) {
+ network_status->SignalNetworkStateDetected.connect(
+ this, &AutoReconnect::OnNetworkStateDetected);
+ }
+}
+
+void AutoReconnect::OnNetworkStateDetected(bool was_alive, bool is_alive) {
+ if (is_retrying() && !was_alive && is_alive) {
+ // Reconnect in 1 to 9 seconds (vary the time a little to try to avoid
+ // spikey behavior on network hiccups).
+ StartReconnectTimerWithInterval((rand() % 9 + 1) * kSecsTo100ns);
+ }
+}
+
+int AutoReconnect::seconds_until() const {
+ if (!is_retrying() || !reconnect_timer_->get_timeout_time()) {
+ return 0;
+ }
+ int64 time_until_100ns =
+ reconnect_timer_->get_timeout_time() - GetCurrent100NSTime();
+ if (time_until_100ns < 0) {
+ return 0;
+ }
+
+ // Do a ceiling on the value (to avoid returning before its time)
+ return (time_until_100ns + kSecsTo100ns - 1) / kSecsTo100ns;
+}
+
+void AutoReconnect::StartReconnectTimer() {
+ StartReconnectTimerWithInterval(reconnect_interval_ns_);
+}
+
+void AutoReconnect::StartReconnectTimerWithInterval(time64 interval_ns) {
+ // Don't call StopReconnectTimer because we don't
+ // want other classes to detect that the intermediate state of
+ // the timer being stopped. (We're avoiding the call to SignalTimerStartStop
+ // while reconnect_timer_ is NULL.)
+ if (reconnect_timer_) {
+ reconnect_timer_->Abort();
+ reconnect_timer_ = NULL;
+ }
+ reconnect_timer_ = new Timer(parent_,
+ static_cast<int>(interval_ns / kSecsTo100ns),
+ false); // repeat
+ reconnect_timer_->SignalTimeout.connect(this,
+ &AutoReconnect::DoReconnect);
+ SignalTimerStartStop();
+}
+
+void AutoReconnect::DoReconnect() {
+ reconnect_timer_ = NULL;
+
+ // if timed out again, double autoreconnect time up to 30 minutes
+ reconnect_interval_ns_ *= 2;
+ if (reconnect_interval_ns_ > 30 * kMinsTo100ns) {
+ reconnect_interval_ns_ = 30 * kMinsTo100ns;
+ }
+ SignalStartConnection();
+}
+
+void AutoReconnect::StopReconnectTimer() {
+ if (reconnect_timer_) {
+ reconnect_timer_->Abort();
+ reconnect_timer_ = NULL;
+ SignalTimerStartStop();
+ }
+}
+
+void AutoReconnect::StopDelayedResetTimer() {
+ if (delayed_reset_timer_) {
+ delayed_reset_timer_->Abort();
+ delayed_reset_timer_ = NULL;
+ }
+}
+
+void AutoReconnect::ResetState() {
+ StopDelayedResetTimer();
+ StopReconnectTimer();
+ SetupReconnectInterval();
+}
+
+void AutoReconnect::SetupReconnectInterval() {
+ if (is_idle_) {
+ // If we were idle, start the timer over again (120 - 360 seconds).
+ reconnect_interval_ns_ = (rand() % 240 + 120) * kSecsTo100ns;
+ } else {
+ // If we weren't idle, try the connection 5 - 25 seconds later.
+ reconnect_interval_ns_ = (rand() % 20 + 5) * kSecsTo100ns;
+ }
+}
+
+void AutoReconnect::OnPowerSuspend(bool suspended) {
+ if (suspended) {
+ // When the computer comes back on, ensure that the reconnect
+ // happens quickly (5 - 25 seconds).
+ reconnect_interval_ns_ = (rand() % 20 + 5) * kSecsTo100ns;
+ }
+}
+
+void AutoReconnect::OnClientStateChange(Login::ConnectionState state) {
+ // On any state change, stop the reset timer.
+ StopDelayedResetTimer();
+ switch (state) {
+ case Login::STATE_RETRYING:
+ // do nothing
+ break;
+
+ case Login::STATE_CLOSED:
+ // When the user has been logged out and no auto-reconnect
+ // is happening, then the autoreconnect intervals should be
+ // reset.
+ ResetState();
+ break;
+
+ case Login::STATE_OPENING:
+ StopReconnectTimer();
+ break;
+
+ case Login::STATE_OPENED:
+ // Reset autoreconnect timeout sequence after being connected
+ // for a bit of time. This helps in the case that we are
+ // connecting briefly and then getting disconnect like when
+ // an account hits an abuse limit.
+ StopReconnectTimer();
+ delayed_reset_timer_ = new Timer(parent_,
+ kResetReconnectInfoDelaySec,
+ false); // repeat
+ delayed_reset_timer_->SignalTimeout.connect(this,
+ &AutoReconnect::ResetState);
+ break;
+ }
+}
+} // namespace notifier
diff --git a/chrome/browser/sync/notifier/communicator/auto_reconnect.h b/chrome/browser/sync/notifier/communicator/auto_reconnect.h
new file mode 100644
index 0000000..f4ee4ec
--- /dev/null
+++ b/chrome/browser/sync/notifier/communicator/auto_reconnect.h
@@ -0,0 +1,71 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_SYNC_NOTIFIER_COMMUNICATOR_AUTO_RECONNECT_H_
+#define CHROME_BROWSER_SYNC_NOTIFIER_COMMUNICATOR_AUTO_RECONNECT_H_
+#include <string>
+
+#include "chrome/browser/sync/notifier/base/time.h"
+#include "chrome/browser/sync/notifier/communicator/login.h"
+#include "talk/base/sigslot.h"
+
+namespace talk_base {
+class Task;
+}
+
+namespace notifier {
+class NetworkStatusDetectorTask;
+class Timer;
+
+class AutoReconnect : public sigslot::has_slots<> {
+ public:
+ AutoReconnect(talk_base::Task* parent,
+ NetworkStatusDetectorTask* network_status);
+ void StartReconnectTimer();
+ void StopReconnectTimer();
+ void OnClientStateChange(Login::ConnectionState state);
+
+ // Callback when power is suspended
+ void OnPowerSuspend(bool suspended);
+
+ void set_idle(bool idle) {
+ is_idle_ = idle;
+ }
+
+ // Returns true if the auto-retry is to be done (pending a countdown)
+ bool is_retrying() const {
+ return reconnect_timer_ != NULL;
+ }
+
+ int seconds_until() const;
+ sigslot::signal0<> SignalTimerStartStop;
+ sigslot::signal0<> SignalStartConnection;
+ private:
+ void StartReconnectTimerWithInterval(time64 interval_ns);
+ void DoReconnect();
+ void ResetState();
+ void SetupReconnectInterval();
+ void StopDelayedResetTimer();
+
+ void OnNetworkStateDetected(bool was_alive, bool is_alive);
+
+ time64 reconnect_interval_ns_;
+ Timer* reconnect_timer_;
+ Timer* delayed_reset_timer_;
+ talk_base::Task* parent_;
+
+ bool is_idle_;
+ DISALLOW_COPY_AND_ASSIGN(AutoReconnect);
+};
+
+// Wait 2 seconds until after we actually connect to
+// reset reconnect related items.
+//
+// The reason for this delay is to avoid the situation in which buzz
+// is trying to block the client due to abuse and the client responses
+// by going into rapid reconnect mode, which makes the problem more severe.
+extern const int kResetReconnectInfoDelaySec;
+
+} // namespace notifier
+#endif // CHROME_BROWSER_SYNC_NOTIFIER_COMMUNICATOR_AUTO_RECONNECT_H_
diff --git a/chrome/browser/sync/notifier/communicator/connection_options.cc b/chrome/browser/sync/notifier/communicator/connection_options.cc
new file mode 100644
index 0000000..2d49bb6
--- /dev/null
+++ b/chrome/browser/sync/notifier/communicator/connection_options.cc
@@ -0,0 +1,16 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/sync/notifier/communicator/connection_options.h"
+
+namespace notifier {
+
+ConnectionOptions::ConnectionOptions()
+ : autodetect_proxy_(true),
+ auto_reconnect_(true),
+ proxy_port_(0),
+ use_proxy_auth_(0),
+ allow_unverified_certs_(false) {
+}
+} // namespace notifier
diff --git a/chrome/browser/sync/notifier/communicator/connection_options.h b/chrome/browser/sync/notifier/communicator/connection_options.h
new file mode 100644
index 0000000..6b559f0
--- /dev/null
+++ b/chrome/browser/sync/notifier/communicator/connection_options.h
@@ -0,0 +1,55 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_SYNC_NOTIFIER_COMMUNICATOR_CONNECTION_OPTIONS_H_
+#define CHROME_BROWSER_SYNC_NOTIFIER_COMMUNICATOR_CONNECTION_OPTIONS_H_
+
+#include <string>
+
+#include "talk/base/cryptstring.h"
+#include "talk/base/helpers.h"
+
+namespace notifier {
+
+class ConnectionOptions {
+ public:
+ ConnectionOptions();
+
+ bool autodetect_proxy() const { return autodetect_proxy_; }
+ bool auto_reconnect() const { return auto_reconnect_; }
+ const std::string& proxy_host() const { return proxy_host_; }
+ int proxy_port() const { return proxy_port_; }
+ bool use_proxy_auth() const { return use_proxy_auth_; }
+ const std::string& auth_user() const { return auth_user_; }
+ const talk_base::CryptString& auth_pass() const { return auth_pass_; }
+ bool allow_unverified_certs() const { return allow_unverified_certs_; }
+
+ void set_autodetect_proxy(bool f) { autodetect_proxy_ = f; }
+ void set_auto_reconnect(bool f) { auto_reconnect_ = f; }
+ void set_proxy_host(const std::string& val) { proxy_host_ = val; }
+ void set_proxy_port(int val) { proxy_port_ = val; }
+ void set_use_proxy_auth(bool f) { use_proxy_auth_ = f; }
+ void set_auth_user(const std::string& val) { auth_user_ = val; }
+ void set_auth_pass(const talk_base::CryptString& val) { auth_pass_ = val; }
+
+ // Setting this to true opens a security hole, so it is
+ // *highly* recommended that you don't do this.
+ void set_allow_unverified_certs(bool allow_unverified_certs) {
+ allow_unverified_certs_ = allow_unverified_certs;
+ }
+
+ private:
+ bool autodetect_proxy_;
+ bool auto_reconnect_;
+ std::string proxy_host_;
+ int proxy_port_;
+ bool use_proxy_auth_;
+ std::string auth_user_;
+ talk_base::CryptString auth_pass_;
+ bool allow_unverified_certs_;
+ // allow the copy constructor and operator=
+};
+} // namespace notifier
+
+#endif // CHROME_BROWSER_SYNC_NOTIFIER_COMMUNICATOR_CONNECTION_OPTIONS_H_
diff --git a/chrome/browser/sync/notifier/communicator/connection_settings.cc b/chrome/browser/sync/notifier/communicator/connection_settings.cc
new file mode 100644
index 0000000..320a396
--- /dev/null
+++ b/chrome/browser/sync/notifier/communicator/connection_settings.cc
@@ -0,0 +1,126 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <deque>
+#include <string>
+#include <vector>
+
+#include "chrome/browser/sync/notifier/communicator/connection_settings.h"
+#include "talk/base/helpers.h"
+#include "talk/xmpp/xmppclientsettings.h"
+
+namespace notifier {
+
+class RandomGenerator {
+ public:
+ int operator()(int ceiling) {
+ return static_cast<int>(cricket::CreateRandomId() % ceiling);
+ }
+};
+
+void ConnectionSettings::FillXmppClientSettings(
+ buzz::XmppClientSettings* xcs) const {
+ assert(xcs);
+ xcs->set_protocol(protocol_);
+ xcs->set_server(server_);
+ xcs->set_proxy(proxy_.type);
+ if (proxy_.type != talk_base::PROXY_NONE) {
+ xcs->set_proxy_host(proxy_.address.IPAsString());
+ xcs->set_proxy_port(proxy_.address.port());
+ }
+ if ((proxy_.type != talk_base::PROXY_NONE) && !proxy_.username.empty()) {
+ xcs->set_use_proxy_auth(true);
+ xcs->set_proxy_user(proxy_.username);
+ xcs->set_proxy_pass(proxy_.password);
+ } else {
+ xcs->set_use_proxy_auth(false);
+ }
+}
+
+void ConnectionSettingsList::AddPermutations(const std::string& hostname,
+ const std::vector<uint32>& iplist,
+ int16 port,
+ bool special_port_magic,
+ bool proxy_only) {
+ // randomize the list. This ensures the iplist isn't always
+ // evaluated in the order returned by DNS
+ std::vector<uint32> iplist_random = iplist;
+ RandomGenerator rg;
+ std::random_shuffle(iplist_random.begin(), iplist_random.end(), rg);
+
+ // Put generated addresses in a new deque, then append on the list_, since
+ // there are order dependencies and AddPermutations() may be called more
+ // than once.
+ std::deque<ConnectionSettings> list_temp;
+
+ // Permute addresses for this server. In some cases we haven't resolved the
+ // to ip addresses.
+ talk_base::SocketAddress server(hostname, port, false);
+ if (iplist_random.empty()) {
+ // We couldn't pre-resolve the hostname, so let's hope it will resolve
+ // further down the pipeline (by a proxy, for example).
+ PermuteForAddress(server, special_port_magic, proxy_only, &list_temp);
+ } else {
+ // Generate a set of possibilities for each server address.
+ // Don't do permute duplicates.
+ for (size_t index = 0; index < iplist_random.size(); ++index) {
+ if (std::find(iplist_seen_.begin(), iplist_seen_.end(),
+ iplist_random[index]) != iplist_seen_.end()) {
+ continue;
+ }
+ iplist_seen_.push_back(iplist_random[index]);
+ server.SetResolvedIP(iplist_random[index]);
+ PermuteForAddress(server, special_port_magic, proxy_only, &list_temp);
+ }
+ }
+
+ // Add this list to the instance list
+ while (list_temp.size() != 0) {
+ list_.push_back(list_temp[0]);
+ list_temp.pop_front();
+ }
+}
+
+
+void ConnectionSettingsList::PermuteForAddress(
+ const talk_base::SocketAddress& server,
+ bool special_port_magic,
+ bool proxy_only,
+ std::deque<ConnectionSettings>* list_temp) {
+ assert(list_temp);
+ *(template_.mutable_server()) = server;
+
+ // Use all of the original settings
+ list_temp->push_back(template_);
+
+ // Try alternate port
+ if (special_port_magic) {
+ ConnectionSettings settings(template_);
+ settings.set_protocol(cricket::PROTO_SSLTCP);
+ settings.mutable_server()->SetPort(443);
+ // HTTPS proxies usually require port 443, so try it first
+ if ((template_.proxy().type == talk_base::PROXY_HTTPS) ||
+ (template_.proxy().type == talk_base::PROXY_UNKNOWN)) {
+ list_temp->push_front(settings);
+ } else {
+ list_temp->push_back(settings);
+ }
+ }
+
+ if (!proxy_only) {
+ // Try without the proxy
+ if (template_.proxy().type != talk_base::PROXY_NONE) {
+ ConnectionSettings settings(template_);
+ settings.mutable_proxy()->type = talk_base::PROXY_NONE;
+ list_temp->push_back(settings);
+
+ if (special_port_magic) {
+ settings.set_protocol(cricket::PROTO_SSLTCP);
+ settings.mutable_server()->SetPort(443);
+ list_temp->push_back(settings);
+ }
+ }
+ }
+}
+} // namespace notifier
diff --git a/chrome/browser/sync/notifier/communicator/connection_settings.h b/chrome/browser/sync/notifier/communicator/connection_settings.h
new file mode 100644
index 0000000..d83b1fc
--- /dev/null
+++ b/chrome/browser/sync/notifier/communicator/connection_settings.h
@@ -0,0 +1,78 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_SYNC_NOTIFIER_COMMUNICATOR_CONNECTION_SETTINGS_H_
+#define CHROME_BROWSER_SYNC_NOTIFIER_COMMUNICATOR_CONNECTION_SETTINGS_H_
+#include <deque>
+#include <string>
+#include <vector>
+
+#include "talk/p2p/base/port.h"
+
+namespace buzz {
+ class XmppClientSettings;
+}
+
+namespace notifier {
+
+class ConnectionSettings {
+ public:
+ ConnectionSettings() : protocol_(cricket::PROTO_TCP) {}
+
+ cricket::ProtocolType protocol() { return protocol_; }
+ const talk_base::SocketAddress& server() const { return server_; }
+ const talk_base::ProxyInfo& proxy() const { return proxy_; }
+
+ void set_protocol(cricket::ProtocolType protocol) { protocol_ = protocol; }
+ talk_base::SocketAddress* mutable_server() { return &server_; }
+ talk_base::ProxyInfo* mutable_proxy() { return &proxy_; }
+
+ void FillXmppClientSettings(buzz::XmppClientSettings* xcs) const;
+
+ private:
+ cricket::ProtocolType protocol_; // PROTO_TCP, PROTO_SSLTCP, etc.
+ talk_base::SocketAddress server_; // Server
+ talk_base::ProxyInfo proxy_; // Proxy info
+ // need copy constructor due to use in stl deque
+};
+
+class ConnectionSettingsList {
+ public:
+ ConnectionSettingsList() {}
+
+ void SetProxy(const talk_base::ProxyInfo& proxy) {
+ *(template_.mutable_proxy()) = proxy;
+ }
+
+ const talk_base::ProxyInfo& proxy() const {
+ return template_.proxy();
+ }
+
+ int GetCount() { return list_.size(); }
+ ConnectionSettings* GetSettings(size_t index) { return &list_[index]; }
+
+ void ClearPermutations() {
+ list_.clear();
+ iplist_seen_.clear();
+ }
+
+ void AddPermutations(const std::string& hostname,
+ const std::vector<uint32>& iplist,
+ int16 port,
+ bool special_port_magic,
+ bool proxy_only);
+ private:
+ void PermuteForAddress(const talk_base::SocketAddress& server,
+ bool special_port_magic,
+ bool proxy_only,
+ std::deque<ConnectionSettings>* list_temp);
+
+ ConnectionSettings template_;
+ std::deque<ConnectionSettings> list_;
+ std::vector<uint32> iplist_seen_;
+ DISALLOW_COPY_AND_ASSIGN(ConnectionSettingsList);
+};
+} // namespace notifier
+
+#endif // CHROME_BROWSER_SYNC_NOTIFIER_COMMUNICATOR_CONNECTION_SETTINGS_H_
diff --git a/chrome/browser/sync/notifier/communicator/const_communicator.h b/chrome/browser/sync/notifier/communicator/const_communicator.h
new file mode 100644
index 0000000..79bb92e
--- /dev/null
+++ b/chrome/browser/sync/notifier/communicator/const_communicator.h
@@ -0,0 +1,11 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_SYNC_NOTIFIER_COMMUNICATOR_CONST_COMMUNICATOR_H_
+#define CHROME_BROWSER_SYNC_NOTIFIER_COMMUNICATOR_CONST_COMMUNICATOR_H_
+namespace notifier {
+// The default port for jabber/xmpp communications
+const int kDefaultXmppPort = 5222;
+} // namespace notifier
+#endif // CHROME_BROWSER_SYNC_NOTIFIER_COMMUNICATOR_CONST_COMMUNICATOR_H_
diff --git a/chrome/browser/sync/notifier/communicator/login.cc b/chrome/browser/sync/notifier/communicator/login.cc
new file mode 100644
index 0000000..5614dba
--- /dev/null
+++ b/chrome/browser/sync/notifier/communicator/login.cc
@@ -0,0 +1,361 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <string>
+
+#include "chrome/browser/sync/notifier/communicator/login.h"
+
+#include "chrome/browser/sync/notifier/base/network_status_detector_task.h"
+#include "chrome/browser/sync/notifier/base/time.h"
+#include "chrome/browser/sync/notifier/base/timer.h"
+#include "chrome/browser/sync/notifier/communicator/auto_reconnect.h"
+#include "chrome/browser/sync/notifier/communicator/connection_options.h"
+#include "chrome/browser/sync/notifier/communicator/login_settings.h"
+#include "chrome/browser/sync/notifier/communicator/product_info.h"
+#include "chrome/browser/sync/notifier/communicator/single_login_attempt.h"
+#include "talk/base/common.h"
+#include "talk/base/firewallsocketserver.h"
+#include "talk/base/logging.h"
+#include "talk/base/taskrunner.h"
+#include "talk/xmllite/xmlelement.h"
+#include "talk/xmpp/asyncsocket.h"
+#include "talk/xmpp/prexmppauth.h"
+#include "talk/xmpp/xmppclient.h"
+#include "talk/xmpp/xmppclientsettings.h"
+#include "talk/xmpp/xmppengine.h"
+
+namespace notifier {
+
+// redirect valid for 5 minutes
+static const time64 kRedirectTimeoutNs = 5 * kMinsTo100ns;
+
+// Disconnect if network stays down for more than 10 seconds.
+static const int kDisconnectionDelaySecs = 10;
+
+Login::Login(talk_base::Task* parent,
+ const buzz::XmppClientSettings& user_settings,
+ const ConnectionOptions& options,
+ std::string lang,
+ ServerInformation* server_list,
+ int server_count,
+ NetworkStatusDetectorTask* network_status,
+ talk_base::FirewallManager* firewall,
+ bool no_gaia_auth,
+ bool proxy_only,
+ bool previous_login_successful)
+ : login_settings_(new LoginSettings(user_settings,
+ options,
+ lang,
+ server_list,
+ server_count,
+ firewall,
+ no_gaia_auth,
+ proxy_only)),
+ single_attempt_(NULL),
+ successful_connection_(previous_login_successful),
+ parent_(parent),
+ state_(STATE_OPENING),
+ redirect_time_ns_(0),
+ redirect_port_(0),
+ unexpected_disconnect_occurred_(false),
+ reset_unexpected_timer_(NULL),
+ google_host_(user_settings.host()),
+ google_user_(user_settings.user()),
+ disconnect_timer_(NULL) {
+ if (!network_status) {
+ network_status = NetworkStatusDetectorTask::Create(parent_);
+ if (network_status) {
+ // On linux we don't have an implementation of NetworkStatusDetectorTask.
+ network_status->Start();
+ }
+ }
+ network_status->SignalNetworkStateDetected.connect(
+ this, &Login::OnNetworkStateDetected);
+ auto_reconnect_.reset(new AutoReconnect(parent_, network_status));
+ auto_reconnect_->SignalStartConnection.connect(this,
+ &Login::StartConnection);
+ auto_reconnect_->SignalTimerStartStop.connect(
+ this,
+ &Login::OnAutoReconnectTimerChange);
+ SignalClientStateChange.connect(auto_reconnect_.get(),
+ &AutoReconnect::OnClientStateChange);
+ SignalIdleChange.connect(auto_reconnect_.get(),
+ &AutoReconnect::set_idle);
+ SignalPowerSuspended.connect(auto_reconnect_.get(),
+ &AutoReconnect::OnPowerSuspend);
+}
+
+// defined so that the destructors are executed here (and
+// the corresponding classes don't need to be included in
+// the header file)
+Login::~Login() {
+ if (single_attempt_) {
+ single_attempt_->Abort();
+ single_attempt_ = NULL;
+ }
+}
+
+void Login::StartConnection() {
+ // If there is a server redirect, use it.
+ if (GetCurrent100NSTime() < redirect_time_ns_ + kRedirectTimeoutNs) {
+ // Override server/port with redirect values
+ talk_base::SocketAddress server_override;
+ server_override.SetIP(redirect_server_, false);
+ ASSERT(redirect_port_ != 0);
+ server_override.SetPort(redirect_port_);
+ login_settings_->set_server_override(server_override);
+ } else {
+ login_settings_->clear_server_override();
+ }
+
+ if (single_attempt_) {
+ single_attempt_->Abort();
+ single_attempt_ = NULL;
+ }
+ single_attempt_ = new SingleLoginAttempt(parent_,
+ login_settings_.get(),
+ successful_connection_);
+
+ // Do the signaling hook-ups.
+ single_attempt_->SignalLoginFailure.connect(this, &Login::OnLoginFailure);
+ single_attempt_->SignalRedirect.connect(this, &Login::OnRedirect);
+ single_attempt_->SignalClientStateChange.connect(
+ this,
+ &Login::OnClientStateChange) ;
+ single_attempt_->SignalUnexpectedDisconnect.connect(
+ this,
+ &Login::OnUnexpectedDisconnect);
+ single_attempt_->SignalLogoff.connect(
+ this,
+ &Login::OnLogoff);
+ single_attempt_->SignalNeedAutoReconnect.connect(
+ this,
+ &Login::DoAutoReconnect);
+ SignalLogInput.repeat(single_attempt_->SignalLogInput);
+ SignalLogOutput.repeat(single_attempt_->SignalLogOutput);
+
+ single_attempt_->Start();
+}
+
+const std::string& Login::google_host() const {
+ return google_host_;
+}
+
+const std::string& Login::google_user() const {
+ return google_user_;
+}
+
+const talk_base::ProxyInfo& Login::proxy() const {
+ return proxy_info_;
+}
+
+void Login::OnLoginFailure(const LoginFailure& failure) {
+ auto_reconnect_->StopReconnectTimer();
+ HandleClientStateChange(STATE_CLOSED);
+ SignalLoginFailure(failure);
+}
+
+void Login::OnLogoff() {
+ HandleClientStateChange(STATE_CLOSED);
+}
+
+void Login::OnClientStateChange(buzz::XmppEngine::State state) {
+ ConnectionState new_state = STATE_CLOSED;
+
+ switch (state) {
+ case buzz::XmppEngine::STATE_NONE:
+ case buzz::XmppEngine::STATE_CLOSED:
+ // Ignore the closed state (because
+ // we may be trying the next dns entry).
+ //
+ // But we go to this state for other
+ // signals when there is no retry happening.
+ new_state = state_;
+ break;
+
+ case buzz::XmppEngine::STATE_START:
+ case buzz::XmppEngine::STATE_OPENING:
+ new_state = STATE_OPENING;
+ break;
+
+ case buzz::XmppEngine::STATE_OPEN:
+ new_state = STATE_OPENED;
+ break;
+
+ default:
+ ASSERT(false);
+ break;
+ }
+ HandleClientStateChange(new_state);
+}
+
+void Login::HandleClientStateChange(ConnectionState new_state) {
+ // Do we need to transition between the retrying and closed states?
+ if (auto_reconnect_->is_retrying()) {
+ if (new_state == STATE_CLOSED) {
+ new_state = STATE_RETRYING;
+ }
+ } else {
+ if (new_state == STATE_RETRYING) {
+ new_state = STATE_CLOSED;
+ }
+ }
+
+ if (new_state != state_) {
+ state_ = new_state;
+ if (reset_unexpected_timer_) {
+ reset_unexpected_timer_->Abort();
+ reset_unexpected_timer_ = NULL;
+ }
+
+ if (state_ == STATE_OPENED) {
+ successful_connection_ = true;
+
+ google_host_ = single_attempt_->xmpp_client()->jid().domain();
+ google_user_ = single_attempt_->xmpp_client()->jid().node();
+ proxy_info_ = single_attempt_->proxy();
+
+ reset_unexpected_timer_ = new Timer(parent_,
+ kResetReconnectInfoDelaySec,
+ false); // repeat
+ reset_unexpected_timer_->SignalTimeout.connect(
+ this,
+ &Login::ResetUnexpectedDisconnect);
+ }
+ SignalClientStateChange(state_);
+ }
+}
+
+void Login::OnAutoReconnectTimerChange() {
+ if (!single_attempt_ || !single_attempt_->xmpp_client()) {
+ HandleClientStateChange(STATE_CLOSED);
+ return;
+ }
+ OnClientStateChange(single_attempt_->xmpp_client()->GetState());
+}
+
+buzz::XmppClient* Login::xmpp_client() {
+ if (!single_attempt_) {
+ return NULL;
+ }
+ return single_attempt_->xmpp_client();
+}
+
+int Login::seconds_until_reconnect() const {
+ return auto_reconnect_->seconds_until();
+}
+
+void Login::UseNextConnection() {
+ if (!single_attempt_) {
+ // Just in case, there is an obscure case that causes
+ // this to get called when there is no single_attempt_.
+ return;
+ }
+ single_attempt_->UseNextConnection();
+}
+
+void Login::UseCurrentConnection() {
+ if (!single_attempt_) {
+ // Just in case, there is an obscure case that causes
+ // this to get called when there is no single_attempt_.
+ return;
+ }
+ single_attempt_->UseCurrentConnection();
+}
+
+void Login::OnRedirect(const std::string& redirect_server, int redirect_port) {
+ ASSERT(redirect_port_ != 0);
+
+ redirect_time_ns_ = GetCurrent100NSTime();
+ redirect_server_ = redirect_server;
+ redirect_port_ = redirect_port;
+
+ // Drop the current connection, and start the login process again
+ StartConnection();
+}
+
+void Login::OnUnexpectedDisconnect() {
+ if (reset_unexpected_timer_) {
+ reset_unexpected_timer_->Abort();
+ reset_unexpected_timer_ = NULL;
+ }
+
+ // Start the login process again
+ if (unexpected_disconnect_occurred_) {
+ // If we already have received an unexpected disconnect recently,
+ // then our account may have be jailed due to abuse, so we shouldn't
+ // make the situation worse by trying really hard to reconnect.
+ // Instead, we'll do the autoreconnect route, which has exponential
+ // back-off.
+ DoAutoReconnect();
+ return;
+ }
+ StartConnection();
+ unexpected_disconnect_occurred_ = true;
+}
+
+void Login::ResetUnexpectedDisconnect() {
+ reset_unexpected_timer_ = NULL;
+ unexpected_disconnect_occurred_ = false;
+}
+
+void Login::DoAutoReconnect() {
+ bool allow_auto_reconnect =
+ login_settings_->connection_options().auto_reconnect();
+ // Start the reconnect time before aborting the connection
+ // to ensure that AutoReconnect::is_retrying() is true, so
+ // that the Login doesn't transition to the CLOSED state
+ // (which would cause the reconnection timer to reset
+ // and not double).
+ if (allow_auto_reconnect) {
+ auto_reconnect_->StartReconnectTimer();
+ }
+
+ if (single_attempt_) {
+ single_attempt_->Abort();
+ single_attempt_ = NULL;
+ }
+
+ if (!allow_auto_reconnect) {
+ HandleClientStateChange(STATE_CLOSED);
+ return;
+ }
+}
+
+void Login::OnNetworkStateDetected(bool was_alive, bool is_alive) {
+ if (was_alive && !is_alive) {
+ // Our network connection just went down.
+ // Setup a timer to disconnect. Don't disconnect immediately to avoid
+ // constant connection/disconnection due to flaky network interfaces.
+ ASSERT(disconnect_timer_ == NULL);
+ disconnect_timer_ = new Timer(parent_, kDisconnectionDelaySecs, false);
+ disconnect_timer_->SignalTimeout.connect(this,
+ &Login::OnDisconnectTimeout);
+ } else if (!was_alive && is_alive) {
+ // Our connection has come back up. If we have a disconnect timer going,
+ // abort it so we don't disconnect.
+ if (disconnect_timer_) {
+ disconnect_timer_->Abort();
+ // It will free itself.
+ disconnect_timer_ = NULL;
+ }
+ }
+}
+
+void Login::OnDisconnectTimeout() {
+ disconnect_timer_ = NULL;
+
+ if (state_ != STATE_OPENED) {
+ return;
+ }
+
+ if (single_attempt_) {
+ single_attempt_->Abort();
+ single_attempt_ = NULL;
+ }
+
+ StartConnection();
+}
+
+} // namespace notifier
diff --git a/chrome/browser/sync/notifier/communicator/login.h b/chrome/browser/sync/notifier/communicator/login.h
new file mode 100644
index 0000000..480f52b
--- /dev/null
+++ b/chrome/browser/sync/notifier/communicator/login.h
@@ -0,0 +1,155 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_SYNC_NOTIFIER_COMMUNICATOR_LOGIN_H_
+#define CHROME_BROWSER_SYNC_NOTIFIER_COMMUNICATOR_LOGIN_H_
+#include <string>
+
+#include "chrome/browser/sync/notifier/base/time.h"
+#include "chrome/browser/sync/notifier/gaia_auth/sigslotrepeater.h"
+#include "talk/base/proxyinfo.h"
+#include "talk/base/scoped_ptr.h"
+#include "talk/base/sigslot.h"
+#include "talk/xmpp/xmppengine.h"
+
+namespace buzz {
+class CaptchaChallenge;
+class XmppClient;
+class XmppEngine;
+class XmppClientSettings;
+} // namespace buzz
+
+namespace talk_base {
+class FirewallManager;
+struct ProxyInfo;
+class Task;
+} // namespace talk_base
+
+namespace notifier {
+class AutoReconnect;
+class ConnectionOptions;
+class LoginFailure;
+class LoginSettings;
+class NetworkStatusDetectorTask;
+struct ServerInformation;
+class SingleLoginAttempt;
+class Timer;
+
+// Does the login, keeps it alive (with refreshing cookies
+// and reattempting login when disconnected), figures out
+// what actions to take on the various errors that may occur.
+class Login : public sigslot::has_slots<> {
+ public:
+ // network_status and firewall may be NULL
+ Login(talk_base::Task* parent,
+ const buzz::XmppClientSettings& user_settings,
+ const ConnectionOptions& options,
+ std::string lang,
+ ServerInformation* server_list,
+ int server_count,
+ NetworkStatusDetectorTask* network_status,
+ talk_base::FirewallManager* firewall,
+ bool no_gaia_auth,
+ bool proxy_only,
+ bool previous_login_successful);
+ ~Login();
+
+ enum ConnectionState {
+ STATE_CLOSED,
+ // Same as the closed state but indicates that a countdown is happening
+ // for auto-retrying the connection.
+ STATE_RETRYING,
+ STATE_OPENING,
+ STATE_OPENED,
+ };
+
+ ConnectionState connection_state() const {
+ return state_;
+ }
+
+ void StartConnection();
+ void UseNextConnection();
+ void UseCurrentConnection();
+ buzz::XmppClient* xmpp_client();
+
+ // Start the auto-reconnect. It may not do the auto-reconnect
+ // if auto-reconnect is turned off.
+ void DoAutoReconnect();
+
+ const LoginSettings& login_settings() const {
+ return *(login_settings_.get());
+ }
+
+ // Returns the best guess at the host responsible for
+ // the account (which we use to determine if it is
+ // a dasher account or not).
+ //
+ // After login this may return a more accurate answer,
+ // which accounts for open sign-up accounts.
+ const std::string& google_host() const;
+
+ // Analogous to google_host but for the user account.
+ // ("fred" in "fred@gmail.com")
+ const std::string& google_user() const;
+
+ // Returns the proxy that is being used to connect (or
+ // the default proxy information if all attempted
+ // connections failed).
+ //
+ // Do not call until StartConnection has been called.
+ const talk_base::ProxyInfo& proxy() const;
+
+ int seconds_until_reconnect() const;
+
+ // SignalClientStateChange(ConnectionState new_state);
+ sigslot::signal1<ConnectionState> SignalClientStateChange;
+
+ sigslot::signal1<const LoginFailure&> SignalLoginFailure;
+ sigslot::repeater2<const char*, int> SignalLogInput;
+ sigslot::repeater2<const char*, int> SignalLogOutput;
+ sigslot::repeater1<bool> SignalIdleChange;
+
+ // The creator should hook this up to a signal that indicates when the power
+ // is being suspended.
+ sigslot::repeater1<bool> SignalPowerSuspended;
+
+ private:
+ void OnRedirect(const std::string& redirect_server, int redirect_port);
+ void OnUnexpectedDisconnect();
+ void OnClientStateChange(buzz::XmppEngine::State state);
+ void OnLoginFailure(const LoginFailure& failure);
+ void OnLogoff();
+ void OnAutoReconnectTimerChange();
+
+ void HandleClientStateChange(ConnectionState new_state);
+ void ResetUnexpectedDisconnect();
+
+ void OnNetworkStateDetected(bool was_alive, bool is_alive);
+ void OnDisconnectTimeout();
+
+ scoped_ptr<LoginSettings> login_settings_;
+ scoped_ptr<AutoReconnect> auto_reconnect_;
+ SingleLoginAttempt* single_attempt_;
+ bool successful_connection_;
+ talk_base::Task* parent_;
+
+ ConnectionState state_;
+
+ // server redirect information
+ time64 redirect_time_ns_;
+ std::string redirect_server_;
+ int redirect_port_;
+ bool unexpected_disconnect_occurred_;
+ Timer* reset_unexpected_timer_;
+ std::string google_host_;
+ std::string google_user_;
+ talk_base::ProxyInfo proxy_info_;
+
+ Timer* disconnect_timer_;
+
+ DISALLOW_COPY_AND_ASSIGN(Login);
+};
+} // namespace notifier
+
+#endif // CHROME_BROWSER_SYNC_NOTIFIER_COMMUNICATOR_LOGIN_H_
diff --git a/chrome/browser/sync/notifier/communicator/login_failure.cc b/chrome/browser/sync/notifier/communicator/login_failure.cc
new file mode 100644
index 0000000..6f29d87
--- /dev/null
+++ b/chrome/browser/sync/notifier/communicator/login_failure.cc
@@ -0,0 +1,45 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/sync/notifier/communicator/login_failure.h"
+
+#include "talk/xmpp/prexmppauth.h"
+
+namespace notifier {
+
+LoginFailure::LoginFailure(LoginError error)
+ : error_(error),
+ xmpp_error_(buzz::XmppEngine::ERROR_NONE),
+ subcode_(0) {
+}
+
+LoginFailure::LoginFailure(LoginError error,
+ buzz::XmppEngine::Error xmpp_error,
+ int subcode)
+ : error_(error),
+ xmpp_error_(xmpp_error),
+ subcode_(subcode) {
+}
+
+LoginFailure::LoginFailure(LoginError error,
+ buzz::XmppEngine::Error xmpp_error,
+ int subcode,
+ const buzz::CaptchaChallenge& captcha)
+ : error_(error),
+ xmpp_error_(xmpp_error),
+ subcode_(subcode),
+ captcha_(new buzz::CaptchaChallenge(captcha)) {
+}
+
+buzz::XmppEngine::Error LoginFailure::xmpp_error() const {
+ ASSERT(error_ == XMPP_ERROR);
+ return xmpp_error_;
+}
+
+const buzz::CaptchaChallenge& LoginFailure::captcha() const {
+ ASSERT(xmpp_error_ == buzz::XmppEngine::ERROR_UNAUTHORIZED ||
+ xmpp_error_ == buzz::XmppEngine::ERROR_MISSING_USERNAME);
+ return *captcha_.get();
+}
+} // namespace notifier
diff --git a/chrome/browser/sync/notifier/communicator/login_failure.h b/chrome/browser/sync/notifier/communicator/login_failure.h
new file mode 100644
index 0000000..cbddc00
--- /dev/null
+++ b/chrome/browser/sync/notifier/communicator/login_failure.h
@@ -0,0 +1,69 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_SYNC_NOTIFIER_COMMUNICATOR_LOGIN_FAILURE_H_
+#define CHROME_BROWSER_SYNC_NOTIFIER_COMMUNICATOR_LOGIN_FAILURE_H_
+
+#include "talk/base/common.h"
+#include "talk/xmpp/xmppengine.h"
+
+namespace buzz {
+class CaptchaChallenge;
+}
+
+namespace notifier {
+
+class LoginFailure {
+ public:
+ enum LoginError {
+ // Check the xmpp_error for more information
+ XMPP_ERROR,
+
+ // If the certificate has expired, it usually means that the
+ // computer's clock isn't set correctly.
+ CERTIFICATE_EXPIRED_ERROR,
+
+ // Apparently, there is a proxy that needs authentication information.
+ PROXY_AUTHENTICATION_ERROR,
+ };
+
+ LoginFailure(LoginError error);
+ LoginFailure(LoginError error,
+ buzz::XmppEngine::Error xmpp_error,
+ int subcode);
+ LoginFailure(LoginError error,
+ buzz::XmppEngine::Error xmpp_error,
+ int subcode,
+ const buzz::CaptchaChallenge& captcha);
+
+ // Used as the first level of error information
+ LoginError error() const {
+ return error_;
+ }
+
+ // Returns the XmppEngine only. Valid if and only if error() == XMPP_ERROR
+ //
+ // Handler should mimic logic from PhoneWindow::ShowConnectionError
+ // (except that the DiagnoseConnectionError has already been done).
+ buzz::XmppEngine::Error xmpp_error() const;
+
+ // Returns the captcha challenge. Valid if and only if
+ // xmpp_error is buzz::XmppEngine::ERROR_UNAUTHORIZED or
+ // buzz::XmppEngine::ERROR_MISSING_USERNAME
+ //
+ // See PhoneWindow::HandleConnectionPasswordError for how to handle this
+ // (after the if (..) { LoginAccountAndConnectionSetting(); ...} because
+ // that is done by SingleLoginAttempt.
+ const buzz::CaptchaChallenge& captcha() const;
+
+ private:
+ LoginError error_;
+ buzz::XmppEngine::Error xmpp_error_;
+ int subcode_;
+ scoped_ptr<buzz::CaptchaChallenge> captcha_;
+
+ DISALLOW_COPY_AND_ASSIGN(LoginFailure);
+};
+} // namespace notifier
+#endif // CHROME_BROWSER_SYNC_NOTIFIER_COMMUNICATOR_LOGIN_FAILURE_H_
diff --git a/chrome/browser/sync/notifier/communicator/login_settings.cc b/chrome/browser/sync/notifier/communicator/login_settings.cc
new file mode 100644
index 0000000..2983dd8
--- /dev/null
+++ b/chrome/browser/sync/notifier/communicator/login_settings.cc
@@ -0,0 +1,57 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <string>
+
+#include "chrome/browser/sync/notifier/communicator/login_settings.h"
+
+#include "chrome/browser/sync/notifier/communicator/connection_options.h"
+#include "chrome/browser/sync/notifier/communicator/xmpp_connection_generator.h"
+#include "talk/base/common.h"
+#include "talk/base/socketaddress.h"
+#include "talk/xmpp/xmppclientsettings.h"
+
+namespace notifier {
+
+LoginSettings::LoginSettings(const buzz::XmppClientSettings& user_settings,
+ const ConnectionOptions& options,
+ std::string lang,
+ ServerInformation* server_list,
+ int server_count,
+ talk_base::FirewallManager* firewall,
+ bool no_gaia_auth,
+ bool proxy_only)
+ : proxy_only_(proxy_only),
+ no_gaia_auth_(no_gaia_auth),
+ firewall_(firewall),
+ lang_(lang),
+ server_list_(new ServerInformation[server_count]),
+ server_count_(server_count),
+ user_settings_(new buzz::XmppClientSettings(user_settings)),
+ connection_options_(new ConnectionOptions(options)) {
+ // Note: firewall may be NULL
+ ASSERT(server_list != 0);
+ ASSERT(server_count > 0);
+ for (int i = 0; i < server_count_; ++i) {
+ server_list_[i] = server_list[i];
+ }
+}
+
+// defined so that the destructors are executed here (and
+// the corresponding classes don't need to be included in
+// the header file)
+LoginSettings::~LoginSettings() {
+}
+
+void LoginSettings::set_server_override(
+ const talk_base::SocketAddress& server) {
+ server_override_.reset(new ServerInformation());
+ server_override_->server = server;
+ server_override_->special_port_magic = server_list_[0].special_port_magic;
+}
+
+void LoginSettings::clear_server_override() {
+ server_override_.reset();
+}
+} // namespace notifier
diff --git a/chrome/browser/sync/notifier/communicator/login_settings.h b/chrome/browser/sync/notifier/communicator/login_settings.h
new file mode 100644
index 0000000..3e9b971
--- /dev/null
+++ b/chrome/browser/sync/notifier/communicator/login_settings.h
@@ -0,0 +1,97 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_SYNC_NOTIFIER_COMMUNICATOR_LOGIN_SETTINGS_H_
+#define CHROME_BROWSER_SYNC_NOTIFIER_COMMUNICATOR_LOGIN_SETTINGS_H_
+#include <string>
+
+#include "chrome/browser/sync/notifier/communicator/xmpp_connection_generator.h"
+#include "talk/base/scoped_ptr.h"
+
+namespace buzz {
+class XmppClientSettings;
+}
+
+namespace talk_base {
+class FirewallManager;
+class SocketAddress;
+}
+
+namespace notifier {
+class ConnectionOptions;
+struct ServerInformation;
+
+class LoginSettings {
+ public:
+ LoginSettings(const buzz::XmppClientSettings& user_settings,
+ const ConnectionOptions& options,
+ std::string lang,
+ ServerInformation* server_list,
+ int server_count,
+ talk_base::FirewallManager* firewall,
+ bool no_gaia_auth,
+ bool proxy_only);
+
+ ~LoginSettings();
+
+ // Note: firewall() may return NULL.
+ //
+ // Could be a const method, but it allows
+ // modification of part (FirewallManager) of its state.
+ talk_base::FirewallManager* firewall() {
+ return firewall_;
+ }
+
+ bool no_gaia_auth() const {
+ return no_gaia_auth_;
+ }
+
+ bool proxy_only() const {
+ return proxy_only_;
+ }
+
+ const std::string& lang() const {
+ return lang_;
+ }
+
+ const ServerInformation* server_list() const {
+ return server_override_.get() ? server_override_.get() : server_list_.get();
+ }
+
+ int server_count() const {
+ return server_override_.get() ? 1 : server_count_;
+ }
+
+ const buzz::XmppClientSettings& user_settings() const {
+ return *user_settings_.get();
+ }
+
+ buzz::XmppClientSettings* modifiable_user_settings() {
+ return user_settings_.get();
+ }
+
+ const ConnectionOptions& connection_options() const {
+ return *connection_options_.get();
+ }
+
+ void set_server_override(const talk_base::SocketAddress& server);
+ void clear_server_override();
+
+ private:
+ bool proxy_only_;
+ bool no_gaia_auth_;
+ talk_base::FirewallManager* firewall_;
+ std::string lang_;
+
+ talk_base::scoped_array<ServerInformation> server_list_;
+ int server_count_;
+ // Used to handle redirects
+ scoped_ptr<ServerInformation> server_override_;
+
+ scoped_ptr<buzz::XmppClientSettings> user_settings_;
+ scoped_ptr<ConnectionOptions> connection_options_;
+ DISALLOW_COPY_AND_ASSIGN(LoginSettings);
+};
+} // namespace notifier
+#endif // CHROME_BROWSER_SYNC_NOTIFIER_COMMUNICATOR_LOGIN_SETTINGS_H_
diff --git a/chrome/browser/sync/notifier/communicator/mailbox.cc b/chrome/browser/sync/notifier/communicator/mailbox.cc
new file mode 100644
index 0000000..22b6690
--- /dev/null
+++ b/chrome/browser/sync/notifier/communicator/mailbox.cc
@@ -0,0 +1,682 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/sync/notifier/communicator/mailbox.h"
+
+#include <assert.h>
+#include <stdlib.h>
+
+#include <stack>
+#include <vector>
+
+#include "chrome/browser/sync/notifier/base/string.h"
+#include "chrome/browser/sync/notifier/base/utils.h"
+#include "chrome/browser/sync/notifier/communicator/xml_parse_helpers.h"
+#include "talk/base/basictypes.h"
+#include "talk/base/common.h"
+#include "talk/base/stringutils.h"
+#include "talk/xmllite/xmlelement.h"
+#include "talk/xmpp/constants.h"
+
+namespace notifier {
+
+// Labels are a list of strings seperated by a '|' character.
+// The '|' character is escaped with a backslash ('\\') and the
+// backslash is also escaped with a backslash.
+static void ParseLabelSet(const std::string& text,
+ MessageThread::StringSet* labels) {
+ const char* input_cur = text.c_str();
+ const char* input_end = input_cur + text.size();
+ char* result = new char[text.size() + 1];
+ char* next_write = result;
+
+ while(input_cur < input_end) {
+ if (*input_cur == '|') {
+ if (next_write != result) {
+ *next_write = '\0';
+ labels->insert(std::string(result));
+ next_write = result;
+ }
+ input_cur++;
+ continue;
+ }
+
+ if (*input_cur == '\\') {
+ // skip a character in the input and break if we are at the end
+ input_cur++;
+ if (input_cur >= input_end)
+ break;
+ }
+ *next_write = *input_cur;
+ next_write++;
+ input_cur++;
+ }
+
+ if (next_write != result) {
+ *next_write = '\0';
+ labels->insert(std::string(result));
+ }
+
+ delete [] result;
+}
+
+// -----------------------------------------------------------------------------
+
+std::string MailAddress::safe_name() const {
+ if (!name().empty()) {
+ return name();
+ }
+
+ if (!address().empty()) {
+ size_t at = address().find('@');
+ if (at == std::string::npos) {
+ return address();
+ }
+
+ if (at != 0) {
+ return address().substr(0, at);
+ }
+ }
+
+ return std::string("(unknown)");
+}
+
+// -----------------------------------------------------------------------------
+MessageThread::~MessageThread() {
+ Clear();
+}
+
+void MessageThread::Clear() {
+ delete labels_;
+ labels_ = NULL;
+
+ delete senders_;
+ senders_ = NULL;
+}
+
+MessageThread& MessageThread::operator=(const MessageThread& r) {
+ if (&r != this) {
+ Clear();
+ // Copy everything
+ r.AssertValid();
+ thread_id_ = r.thread_id_;
+ date64_ = r.date64_;
+ message_count_ = r.message_count_;
+ personal_level_ = r.personal_level_;
+ subject_ = r.subject_;
+ snippet_ = r.snippet_;
+
+ if (r.labels_)
+ labels_ = new StringSet(*r.labels_);
+ else
+ labels_ = new StringSet;
+ if (r.senders_)
+ senders_ = new MailSenderList(*r.senders_);
+ else
+ senders_ = new MailSenderList;
+ }
+ AssertValid();
+ return *this;
+}
+
+MessageThread* MessageThread::CreateFromXML(
+ const buzz::XmlElement* src) {
+ MessageThread* info = new MessageThread();
+ if (!info || !info->InitFromXml(src)) {
+ delete info;
+ return NULL;
+ }
+ return info;
+}
+
+// Init from a chunk of XML
+bool MessageThread::InitFromXml(const buzz::XmlElement* src) {
+ labels_ = new StringSet;
+ senders_ = new MailSenderList;
+
+ if (src->Name() != buzz::kQnMailThreadInfo)
+ return false;
+
+ if (!ParseInt64Attr(src, buzz::kQnMailTid, &thread_id_))
+ return false;
+ if (!ParseInt64Attr(src, buzz::kQnMailDate, &date64_))
+ return false;
+ if (!ParseIntAttr(src, buzz::kQnMailMessages, &message_count_))
+ return false;
+ if (!ParseIntAttr(src, buzz::kQnMailParticipation, &personal_level_))
+ return false;
+
+ const buzz::XmlElement* senders = src->FirstNamed(buzz::kQnMailSenders);
+ if (!senders)
+ return false;
+ for (const buzz::XmlElement* child = senders->FirstElement();
+ child != NULL;
+ child = child->NextElement()) {
+ if (child->Name() != buzz::kQnMailSender)
+ continue;
+ std::string address;
+ if (!ParseStringAttr(child, buzz::kQnMailAddress, &address))
+ continue;
+ std::string name;
+ ParseStringAttr(child, buzz::kQnMailName, &name);
+ bool originator = false;
+ ParseBoolAttr(child, buzz::kQnMailOriginator, &originator);
+ bool unread = false;
+ ParseBoolAttr(child, buzz::kQnMailUnread, &unread);
+
+ senders_->push_back(MailSender(name, address, unread, originator));
+ }
+
+ const buzz::XmlElement* labels = src->FirstNamed(buzz::kQnMailLabels);
+ if (!labels)
+ return false;
+ ParseLabelSet(labels->BodyText(), labels_);
+
+ const buzz::XmlElement* subject = src->FirstNamed(buzz::kQnMailSubject);
+ if (!subject)
+ return false;
+ subject_ = subject->BodyText();
+
+ const buzz::XmlElement* snippet = src->FirstNamed(buzz::kQnMailSnippet);
+ if (!snippet)
+ return false;
+ snippet_ = snippet->BodyText();
+
+ AssertValid();
+ return true;
+}
+
+bool MessageThread::starred() const {
+ return (labels_->find("^t") != labels_->end());
+}
+
+bool MessageThread::unread() const {
+ return (labels_->find("^u") != labels_->end());
+}
+
+#ifdef _DEBUG
+// non-debug version is inline and empty
+void MessageThread::AssertValid() const {
+ assert(thread_id_ != 0);
+ assert(senders_ != NULL);
+ // In some (odd) cases, gmail may send email with no sender.
+ // assert(!senders_->empty());
+ assert(message_count_ > 0);
+ assert(labels_ != NULL);
+}
+#endif
+
+
+
+MailBox* MailBox::CreateFromXML(const buzz::XmlElement* src) {
+ MailBox* mail_box = new MailBox();
+ if (!mail_box || !mail_box->InitFromXml(src)) {
+ delete mail_box;
+ return NULL;
+ }
+ return mail_box;
+}
+
+// Init from a chunk of XML
+bool MailBox::InitFromXml(const buzz::XmlElement* src)
+{
+ if (src->Name() != buzz::kQnMailBox)
+ return false;
+
+ if (!ParseIntAttr(src, buzz::kQnMailTotalMatched, &mailbox_size_))
+ return false;
+
+ estimate_ = false;
+ ParseBoolAttr(src, buzz::kQnMailTotalEstimate, &estimate_);
+
+ first_index_ = 0;
+ ParseIntAttr(src, buzz::kQnMailFirstIndex, &first_index_);
+
+ result_time_ = 0;
+ ParseInt64Attr(src, buzz::kQnMailResultTime, &result_time_);
+
+ highest_thread_id_ = 0;
+
+ const buzz::XmlElement* thread_element = src->FirstNamed(buzz::kQnMailThreadInfo);
+ while (thread_element) {
+ MessageThread* thread = MessageThread::CreateFromXML(thread_element);
+ if (thread) {
+ if (thread->thread_id() > highest_thread_id_)
+ highest_thread_id_ = thread->thread_id();
+ threads_.push_back(MessageThreadPointer(thread));
+ }
+ thread_element = thread_element->NextNamed(buzz::kQnMailThreadInfo);
+ }
+ return true;
+}
+
+const size_t kMaxShortnameLength = 12;
+
+// Tip: If you extend this list of chars, do not include '-'
+const char name_delim[] = " ,.:;\'\"()[]{}<>*@";
+
+class SenderFormatter {
+ public:
+ // sender should not be deleted while this class is in use.
+ SenderFormatter(const MailSender& sender,
+ const std::string& me_address)
+ : sender_(sender),
+ visible_(false),
+ short_format_(true),
+ space_(kMaxShortnameLength) {
+ me_ = talk_base::ascicmp(me_address.c_str(),
+ sender.address().c_str()) == 0;
+ }
+
+ bool visible() const {
+ return visible_;
+ }
+
+ bool is_unread() const {
+ return sender_.unread();
+ }
+
+ const std::string name() const {
+ return name_;
+ }
+
+ void set_short_format(bool short_format) {
+ short_format_ = short_format;
+ UpdateName();
+ }
+
+ void set_visible(bool visible) {
+ visible_ = visible;
+ UpdateName();
+ }
+
+ void set_space(size_t space) {
+ space_ = space;
+ UpdateName();
+ }
+
+ private:
+ // Attempt to shorten to the first word in a person's name
+ // We could revisit and do better at international punctuation,
+ // but this is what cricket did, and it should be removed
+ // soon when gmail does the notification instead of us
+ // forming it on the client.
+ static void ShortenName(std::string* name) {
+ size_t start = name->find_first_not_of(name_delim);
+ if (start != std::string::npos && start > 0) {
+ name->erase(0, start);
+ }
+ start = name->find_first_of(name_delim);
+ if (start != std::string::npos) {
+ name->erase(start);
+ }
+ }
+
+ void UpdateName() {
+ // Update the name if is going to be used.
+ if (!visible_) {
+ return;
+ }
+
+ if (me_) {
+ name_ = "me";
+ return;
+ }
+
+ if (sender_.name().empty() && sender_.address().empty()) {
+ name_ = "";
+ return;
+ }
+
+ name_ = sender_.name();
+ // Handle the case of no name or a name looks like an email address.
+ // When mail is sent to "Quality@example.com" <quality-team@example.com>,
+ // we shouldn't show "Quality@example.com" as the name.
+ // Instead use the email address (without the @...)
+ if (name_.empty() || name_.find_first_of("@") != std::string::npos) {
+ name_ = sender_.address();
+ size_t at_index = name_.find_first_of("@");
+ if (at_index != std::string::npos) {
+ name_.erase(at_index);
+ }
+ } else if (short_format_) {
+ ShortenName(&name_);
+ }
+
+ if (name_.empty()) {
+ name_ = "(unknown)";
+ }
+
+ // Abbreviate if too long.
+ if (name_.size() > space_) {
+ name_.replace(space_ - 1, std::string::npos, ".");
+ }
+ }
+
+ const MailSender& sender_;
+ std::string name_;
+ bool visible_;
+ bool short_format_;
+ size_t space_;
+ bool me_;
+ DISALLOW_COPY_AND_ASSIGN(SenderFormatter);
+};
+
+const char kNormalSeparator[] = ",&nbsp;";
+const char kEllidedSeparator[] = "&nbsp;..";
+
+std::string FormatName(const std::string& name, bool bold) {
+ std::string formatted_name;
+ if (bold) {
+ formatted_name.append("<b>");
+ }
+ formatted_name.append(HtmlEncode(name));
+ if (bold) {
+ formatted_name.append("</b>");
+ }
+ return formatted_name;
+}
+
+class SenderFormatterList {
+ public:
+ // sender_list must not be deleted while this class is being used.
+ SenderFormatterList(const MailSenderList& sender_list,
+ const std::string& me_address)
+ : state_(INITIAL_STATE),
+ are_any_read_(false),
+ index_(-1),
+ first_unread_index_(-1) {
+ // Add all read messages.
+ const MailSender* originator = NULL;
+ bool any_unread = false;
+ for (size_t i = 0; i < sender_list.size(); ++i) {
+ if (sender_list[i].originator()) {
+ originator = &sender_list[i];
+ }
+ if (sender_list[i].unread()) {
+ any_unread = true;
+ continue;
+ }
+ are_any_read_ = true;
+ if (!sender_list[i].originator()) {
+ senders_.push_back(new SenderFormatter(sender_list[i],
+ me_address));
+ }
+ }
+
+ // There may not be an orignator (if there no senders).
+ if (originator) {
+ senders_.insert(senders_.begin(), new SenderFormatter(*originator,
+ me_address));
+ }
+
+ // Add all unread messages.
+ if (any_unread) {
+ // It should be rare, but there may be cases when all of the
+ // senders appear to have read the message.
+ first_unread_index_ = is_first_unread() ? 0 : senders_.size();
+ for (size_t i = 0; i < sender_list.size(); ++i) {
+ if (!sender_list[i].unread()) {
+ continue;
+ }
+ // Don't add the originator if it is already at the
+ // start of the "unread" list.
+ if (sender_list[i].originator() && is_first_unread()) {
+ continue;
+ }
+ senders_.push_back(new SenderFormatter(sender_list[i],
+ me_address));
+ }
+ }
+ }
+
+ ~SenderFormatterList() {
+ CleanupSequence(&senders_);
+ }
+
+ std::string GetHtml(int space) {
+ index_ = -1;
+ state_ = INITIAL_STATE;
+ while (!added_.empty()) {
+ added_.pop();
+ }
+
+ // If there is only one sender, let it take up all of the space.
+ if (senders_.size() == 1) {
+ senders_[0]->set_space(space);
+ senders_[0]->set_short_format(false);
+ }
+
+ int length = 1;
+ // Add as many senders as we can in the given space.
+ // Computes the visible length at each iteration,
+ // but does not construct the actual html.
+ while (length < space && AddNextSender()) {
+ int new_length = ConstructHtml(is_first_unread(), NULL);
+ // Remove names to avoid truncating
+ // * if there will be at least 2 left or
+ // * if the spacing <= 2 characters per sender and there
+ // is at least one left.
+ if ((new_length > space &&
+ (visible_count() > 2 ||
+ (ComputeSpacePerSender(space) <= 2 && visible_count() > 1)))) {
+ RemoveLastAddedSender();
+ break;
+ }
+ length = new_length;
+ }
+
+ if (length > space) {
+ int max = ComputeSpacePerSender(space);
+ for (size_t i = 0; i < senders_.size(); ++i) {
+ if (senders_[i]->visible()) {
+ senders_[i]->set_space(max);
+ }
+ }
+ }
+
+ // Now construct the actual html
+ std::string html_list;
+ length = ConstructHtml(is_first_unread(), &html_list);
+ if (length > space) {
+ LOG(LS_WARNING) << "LENGTH: " << length << " exceeds "
+ << space << " " << html_list;
+ }
+ return html_list;
+ }
+
+ private:
+ int ComputeSpacePerSender(int space) const {
+ // Why the "- 2"? To allow for the " .. " which may occur
+ // after the names, and no matter what always allow at least
+ // 2 characters per sender.
+ return talk_base::_max<int>(space / visible_count() - 2, 2);
+ }
+
+ // Finds the next sender that should be added to the "from" list
+ // and sets it to visible.
+ //
+ // This method may be called until it returns false or
+ // until RemoveLastAddedSender is called.
+ bool AddNextSender() {
+ // The progression is:
+ // 1. Add the person who started the thread, which is the first message.
+ // 2. Add the first unread message (unless it has already been added).
+ // 3. Add the last message (unless it has already been added).
+ // 4. Add the message that is just before the last message processed
+ // (unless it has already been added).
+ // If there is no message (i.e. at index -1), return false.
+ //
+ // Typically, this method is called until it returns false or
+ // all of the space available is used.
+ switch (state_) {
+ case INITIAL_STATE:
+ state_ = FIRST_MESSAGE;
+ index_ = 0;
+ // If the server behaves odd and doesn't send us any senders,
+ // do something graceful.
+ if (senders_.size() == 0) {
+ return false;
+ }
+ break;
+
+ case FIRST_MESSAGE:
+ if (first_unread_index_ >= 0) {
+ state_ = FIRST_UNREAD_MESSAGE;
+ index_ = first_unread_index_;
+ break;
+ }
+ // fall through
+ case FIRST_UNREAD_MESSAGE:
+ state_ = LAST_MESSAGE;
+ index_ = senders_.size() - 1;
+ break;
+
+ case LAST_MESSAGE:
+ case PREVIOUS_MESSAGE:
+ state_ = PREVIOUS_MESSAGE;
+ index_--;
+ break;
+
+ case REMOVED_MESSAGE:
+ default:
+ ASSERT(false);
+ return false;
+ }
+
+ if (index_ < 0) {
+ return false;
+ }
+
+ if (!senders_[index_]->visible()) {
+ added_.push(index_);
+ senders_[index_]->set_visible(true);
+ }
+ return true;
+ }
+
+ // Makes the last added sender not visible.
+ //
+ // May be called while visible_count() > 0.
+ void RemoveLastAddedSender() {
+ state_ = REMOVED_MESSAGE;
+ int index = added_.top();
+ added_.pop();
+ senders_[index]->set_visible(false);
+ }
+
+ // Constructs the html of the SenderList and returns the length of the
+ // visible text.
+ //
+ // The algorithm simply walks down the list of Senders, appending
+ // the html for each visible sender, and adding ellipsis or commas
+ // in between, whichever is appropriate.
+ //
+ // html Filled with html. Maybe NULL if the html doesn't
+ // need to be constructed yet (useful for simply
+ // determining the length of the visible text).
+ //
+ // returns The approximate visible length of the html.
+ int ConstructHtml(bool first_is_unread,
+ std::string* html) const {
+ if (senders_.empty()) {
+ return 0;
+ }
+
+ int length = 0;
+
+ // The first is always visible
+ const SenderFormatter* sender = senders_[0];
+ const std::string& originator_name = sender->name();
+ length += originator_name.length();
+ if (html) {
+ html->append(FormatName(originator_name, first_is_unread));
+ }
+
+ bool elided = false;
+ const char* between = "";
+ for (size_t i = 1; i < senders_.size(); i++) {
+ sender = senders_[i];
+
+ if (sender->visible()) {
+ // Handle the separator
+ between = elided ? "&nbsp;" : kNormalSeparator;
+ // Ignore the , for length because it is so narrow,
+ // so in both cases above the space is the only things
+ // that counts for spaces.
+ length++;
+
+ // Handle the name
+ const std::string name = sender->name();
+ length += name.size();
+
+ // Construct the html
+ if (html) {
+ html->append(between);
+ html->append(FormatName(name, sender->is_unread()));
+ }
+ elided = false;
+ } else if (!elided) {
+ between = kEllidedSeparator;
+ length += 2; // ".." is narrow
+ if (html) {
+ html->append(between);
+ }
+ elided = true;
+ }
+ }
+ return length;
+ }
+
+ bool is_first_unread() const {
+ return !are_any_read_;
+ }
+
+ size_t visible_count() const {
+ return added_.size();
+ }
+
+ enum MessageState {
+ INITIAL_STATE,
+ FIRST_MESSAGE,
+ FIRST_UNREAD_MESSAGE,
+ LAST_MESSAGE,
+ PREVIOUS_MESSAGE,
+ REMOVED_MESSAGE,
+ };
+
+ // What state we were in during the last "stateful" function call.
+ MessageState state_;
+ bool are_any_read_;
+ std::vector<SenderFormatter*> senders_;
+ std::stack<int> added_;
+ int index_;
+ int first_unread_index_;
+ DISALLOW_COPY_AND_ASSIGN(SenderFormatterList);
+};
+
+
+std::string GetSenderHtml(const MailSenderList& sender_list,
+ int message_count,
+ const std::string& me_address,
+ int space) {
+ // There has to be at least 9 spaces to show something reasonable.
+ ASSERT(space >= 10);
+ std::string count_html;
+ if (message_count > 1) {
+ std::string count(IntToString(message_count));
+ space -= (count.size());
+ count_html.append("&nbsp;(");
+ count_html.append(count);
+ count_html.append(")");
+ // Reduce space due to parenthesis and &nbsp;.
+ space -= 2;
+ }
+
+ SenderFormatterList senders(sender_list, me_address);
+ std::string html_list(senders.GetHtml(space));
+ html_list.append(count_html);
+ return html_list;
+}
+} // namespace notifier
diff --git a/chrome/browser/sync/notifier/communicator/mailbox.h b/chrome/browser/sync/notifier/communicator/mailbox.h
new file mode 100644
index 0000000..009de73
--- /dev/null
+++ b/chrome/browser/sync/notifier/communicator/mailbox.h
@@ -0,0 +1,166 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_SYNC_NOTIFIER_COMMUNICATOR_MAILBOX_H_
+#define CHROME_BROWSER_SYNC_NOTIFIER_COMMUNICATOR_MAILBOX_H_
+
+#include <set>
+#include <string>
+#include <vector>
+
+#include "talk/base/basictypes.h"
+#include "talk/base/linked_ptr.h"
+
+namespace buzz {
+class XmlElement;
+}
+
+namespace notifier {
+// -----------------------------------------------------------------------------
+class MailAddress {
+ public:
+ MailAddress(const std::string& name, const std::string& address)
+ : name_(name),
+ address_(address) {
+ }
+ const std::string& name() const { return name_; }
+ const std::string& address() const { return address_; }
+ std::string safe_name() const; // will return *something*
+ private:
+ std::string name_;
+ std::string address_;
+};
+
+// -----------------------------------------------------------------------------
+class MailSender : public MailAddress {
+ public:
+ MailSender(const std::string& name, const std::string& address, bool unread,
+ bool originator)
+ : MailAddress(name, address),
+ unread_(unread),
+ originator_(originator) {
+ }
+
+ MailSender(const MailSender& r)
+ : MailAddress(r.name(), r.address()) {
+ unread_ = r.unread_;
+ originator_ = r.originator_;
+ }
+
+ bool unread() const { return unread_; }
+ bool originator() const { return originator_; }
+
+ private:
+ bool unread_;
+ bool originator_;
+};
+
+typedef std::vector<MailSender> MailSenderList;
+
+// -----------------------------------------------------------------------------
+// MessageThread: everything there is to know about a mail thread.
+class MessageThread {
+ public:
+ MessageThread(const MessageThread& r) {
+ labels_ = NULL;
+ senders_ = NULL;
+ *this = r;
+ }
+
+ ~MessageThread();
+
+ // Try to parse the XML to create a MessageThreadInfo. If NULL is returned
+ // then we either ran out of memory or there was an error in parsing the
+ // XML
+ static MessageThread* CreateFromXML(const buzz::XmlElement* src);
+
+ MessageThread& operator=(const MessageThread& r);
+
+ // SameThreadAs : name is self evident
+ bool SameThreadAs(const MessageThread& r) {
+ AssertValid();
+ r.AssertValid();
+ return (thread_id_ == r.thread_id_);
+ }
+
+ // SameAs : true if thread has same id and messages
+ // Assumes that messages don't disappear from threads.
+ bool SameAs(const MessageThread& r) {
+ AssertValid();
+ r.AssertValid();
+ return SameThreadAs(r) &&
+ message_count_ == r.message_count_;
+ }
+
+ typedef std::set<std::string> StringSet;
+
+ int64 thread_id() const { return thread_id_; }
+ const StringSet* labels() const { return labels_; }
+ int64 date64() const { return date64_; }
+ MailSenderList* senders() const { return senders_; }
+ int personal_level() const { return personal_level_; }
+ int message_count() const { return message_count_; }
+ const std::string& subject() const { return subject_; }
+ const std::string& snippet() const { return snippet_; }
+ bool starred() const;
+ bool unread() const;
+
+#ifdef _DEBUG
+ void AssertValid() const;
+#else
+ inline void AssertValid() const {}
+#endif
+
+ private:
+ void Clear();
+
+ private:
+ MessageThread() : senders_(NULL), labels_(NULL) {}
+ bool InitFromXml(const buzz::XmlElement* src);
+
+ int64 thread_id_;
+ int64 date64_;
+ int message_count_;
+ int personal_level_;
+ std::string subject_;
+ std::string snippet_;
+ MailSenderList* senders_;
+ StringSet* labels_;
+};
+
+typedef talk_base::linked_ptr<MessageThread> MessageThreadPointer;
+typedef std::vector<MessageThreadPointer> MessageThreadVector;
+
+// -----------------------------------------------------------------------------
+class MailBox {
+ public:
+ static MailBox* CreateFromXML(const buzz::XmlElement* src);
+
+ const MessageThreadVector& threads() const { return threads_; }
+ int mailbox_size() const { return mailbox_size_; }
+ int first_index() const { return first_index_; }
+ bool estimate() const { return estimate_; }
+ int64 result_time() const { return result_time_; }
+ int64 highest_thread_id() const { return highest_thread_id_; }
+
+ private:
+ MailBox() {}
+ bool InitFromXml(const buzz::XmlElement* src);
+
+ MessageThreadVector threads_;
+
+ int mailbox_size_;
+ int first_index_;
+ bool estimate_;
+ int64 result_time_;
+ int64 highest_thread_id_;
+};
+
+std::string GetSenderHtml(const MailSenderList& sender_list,
+ int message_count,
+ const std::string& me_address,
+ int space);
+} // namespace notifier
+
+#endif // CHROME_BROWSER_SYNC_NOTIFIER_COMMUNICATOR_MAILBOX_H_
diff --git a/chrome/browser/sync/notifier/communicator/mailbox_unittest.cc b/chrome/browser/sync/notifier/communicator/mailbox_unittest.cc
new file mode 100644
index 0000000..1d498d1
--- /dev/null
+++ b/chrome/browser/sync/notifier/communicator/mailbox_unittest.cc
@@ -0,0 +1,118 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/sync/notifier/communicator/mailbox.h"
+#include "notifier/testing/notifier/unittest.h"
+
+namespace notifier {
+TEST_NOTIFIER_F(MailBoxTest);
+
+TEST_F(MailBoxTest, SingleSenderHtml) {
+ std::string me_address("random@company.com");
+ MailSenderList sender_list;
+ sender_list.push_back(MailSender("Alex Smith", "a@a.com", true, true));
+ std::string sender_html = GetSenderHtml(sender_list, 1, me_address, 25);
+ ASSERT_STREQ("<b>Alex Smith</b>", sender_html.c_str());
+}
+
+TEST_F(MailBoxTest, TruncatedSingleSenderHtml) {
+ std::string me_address("random@company.com");
+ MailSenderList sender_list;
+ sender_list.push_back(MailSender(
+ "Alex Smith AReallyLongLastNameThatWillBeTruncated",
+ "a@a.com",
+ true,
+ true));
+ std::string sender_html = GetSenderHtml(sender_list, 1, me_address, 25);
+ ASSERT_STREQ("<b>Alex Smith AReallyLongLa.</b>", sender_html.c_str());
+}
+
+TEST_F(MailBoxTest, SingleSenderManyTimesHtml) {
+ std::string me_address("random@company.com");
+ MailSenderList sender_list;
+ sender_list.push_back(MailSender("Alex Smith", "a@a.com", true, true));
+ std::string sender_html = GetSenderHtml(sender_list, 10, me_address, 25);
+ ASSERT_STREQ("<b>Alex Smith</b>&nbsp;(10)", sender_html.c_str());
+}
+
+TEST_F(MailBoxTest, SenderWithMeHtml) {
+ std::string me_address("randOm@comPany.Com");
+ MailSenderList sender_list;
+ sender_list.push_back(
+ MailSender("Alex Smith", "alex@jones.com", false, false));
+ sender_list.push_back(
+ MailSender("Your Name Goes Here", "raNdom@coMpany.cOm", true, true));
+ std::string sender_html = GetSenderHtml(sender_list, 5, me_address, 25);
+ ASSERT_STREQ("me,&nbsp;Alex,&nbsp;<b>me</b>&nbsp;(5)", sender_html.c_str());
+}
+
+TEST_F(MailBoxTest, SenderHtmlWithAllUnread) {
+ std::string me_address("random@company.com");
+ MailSenderList sender_list;
+ sender_list.push_back(
+ MailSender("Alex Smith", "alex@jones.com", true, false));
+ sender_list.push_back(MailSender(
+ "Your Name Goes Here",
+ "foo@company.com",
+ true,
+ true));
+ sender_list.push_back(MailSender("", "bob@davis.com", true, false));
+ std::string sender_html = GetSenderHtml(sender_list, 100, me_address, 25);
+ ASSERT_STREQ("<b>Your</b>,&nbsp;<b>Alex</b>,&nbsp;<b>bob</b>&nbsp;(100)",
+ sender_html.c_str());
+}
+
+TEST_F(MailBoxTest, SenderHtmlWithTruncatedNames) {
+ std::string me_address("random@company.com");
+ MailSenderList sender_list;
+ sender_list.push_back(MailSender(
+ "ReallyLongName Smith",
+ "alex@jones.com",
+ true,
+ false));
+ sender_list.push_back(MailSender(
+ "AnotherVeryLongFirstNameIsHere",
+ "foo@company.com",
+ true,
+ true));
+ std::string sender_html = GetSenderHtml(sender_list, 2, me_address, 25);
+ ASSERT_STREQ("<b>AnotherV.</b>,&nbsp;<b>ReallyLo.</b>&nbsp;(2)",
+ sender_html.c_str());
+}
+
+TEST_F(MailBoxTest, SenderWithTwoSendersShowing) {
+ std::string me_address("random@company.com");
+ MailSenderList sender_list;
+ sender_list.push_back(
+ MailSender("ALongishName Smith", "alex@jones.com", false, false));
+ sender_list.push_back(
+ MailSender("AnotherBigName", "no@company.com", true, false));
+ sender_list.push_back(
+ MailSender("Person1", "no1@company.com", true, false));
+ sender_list.push_back(
+ MailSender("Person2", "no2@company.com", false, true));
+ std::string sender_html = GetSenderHtml(sender_list, 6, me_address, 25);
+ ASSERT_STREQ("Person2&nbsp;..&nbsp;<b>AnotherB.</b>&nbsp;..&nbsp;(6)",
+ sender_html.c_str());
+}
+
+TEST_F(MailBoxTest, SenderWithThreeSendersShowing) {
+ std::string me_address("random@company.com");
+ MailSenderList sender_list;
+ sender_list.push_back(
+ MailSender("Joe Smith", "alex@jones.com", false, false));
+ sender_list.push_back(
+ MailSender("Bob Other", "no@company.com", true, false));
+ sender_list.push_back(
+ MailSender("Person0", "no0@company.com", true, false));
+ sender_list.push_back(
+ MailSender("Person1", "no1@company.com", true, false));
+ sender_list.push_back(
+ MailSender("ted", "ted@company.com", false, true));
+ std::string sender_html = GetSenderHtml(sender_list, 6, me_address, 25);
+ ASSERT_STREQ(
+ "ted&nbsp;..&nbsp;<b>Bob</b>&nbsp;..&nbsp;<b>Person1</b>&nbsp;(6)",
+ sender_html.c_str());
+}
+} // namespace notifier
diff --git a/chrome/browser/sync/notifier/communicator/product_info.cc b/chrome/browser/sync/notifier/communicator/product_info.cc
new file mode 100644
index 0000000..c1deafb
--- /dev/null
+++ b/chrome/browser/sync/notifier/communicator/product_info.cc
@@ -0,0 +1,15 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <string>
+
+namespace notifier {
+std::string GetUserAgentString() {
+ return kXmppProductName;
+}
+
+std::string GetProductSignature() {
+ return kXmppProductName;
+}
+} // namespace notifier
diff --git a/chrome/browser/sync/notifier/communicator/product_info.h b/chrome/browser/sync/notifier/communicator/product_info.h
new file mode 100644
index 0000000..1da60b0
--- /dev/null
+++ b/chrome/browser/sync/notifier/communicator/product_info.h
@@ -0,0 +1,14 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_SYNC_NOTIFIER_COMMUNICATOR_PRODUCT_INFO_H_
+#define CHROME_BROWSER_SYNC_NOTIFIER_COMMUNICATOR_PRODUCT_INFO_H_
+#include <string>
+
+namespace notifier {
+std::string GetUserAgentString();
+std::string GetProductSignature();
+} // namespace notifier
+
+#endif // CHROME_BROWSER_SYNC_NOTIFIER_COMMUNICATOR_PRODUCT_INFO_H_
diff --git a/chrome/browser/sync/notifier/communicator/single_login_attempt.cc b/chrome/browser/sync/notifier/communicator/single_login_attempt.cc
new file mode 100644
index 0000000..68fe272
--- /dev/null
+++ b/chrome/browser/sync/notifier/communicator/single_login_attempt.cc
@@ -0,0 +1,562 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <string>
+
+#include "chrome/browser/sync/notifier/communicator/single_login_attempt.h"
+
+#include "chrome/browser/sync/notifier/communicator/connection_options.h"
+#include "chrome/browser/sync/notifier/communicator/connection_settings.h"
+#include "chrome/browser/sync/notifier/communicator/const_communicator.h"
+#include "chrome/browser/sync/notifier/communicator/login_failure.h"
+#include "chrome/browser/sync/notifier/communicator/login_settings.h"
+#include "chrome/browser/sync/notifier/communicator/product_info.h"
+#include "chrome/browser/sync/notifier/communicator/xmpp_connection_generator.h"
+#include "chrome/browser/sync/notifier/communicator/xmpp_socket_adapter.h"
+#include "chrome/browser/sync/notifier/gaia_auth/gaiaauth.h"
+#include "talk/base/asynchttprequest.h"
+#include "talk/base/firewallsocketserver.h"
+#include "talk/base/signalthread.h"
+#include "talk/base/taskrunner.h"
+#include "talk/xmllite/xmlelement.h"
+#include "talk/xmpp/constants.h"
+#include "talk/xmpp/prexmppauth.h"
+#include "talk/xmpp/xmppclient.h"
+#include "talk/xmpp/xmppclientsettings.h"
+
+namespace notifier {
+static void FillProxyInfo(const buzz::XmppClientSettings& xcs,
+ talk_base::ProxyInfo* proxy) {
+ ASSERT(proxy != NULL);
+ proxy->type = xcs.proxy();
+ proxy->address.SetIP(xcs.proxy_host());
+ proxy->address.SetPort(xcs.proxy_port());
+ if (xcs.use_proxy_auth()) {
+ proxy->username = xcs.proxy_user();
+ proxy->password = xcs.proxy_pass();
+ }
+}
+
+static void GetClientErrorInformation(
+ buzz::XmppClient* client,
+ buzz::XmppEngine::Error* error,
+ int* subcode,
+ buzz::XmlElement** stream_error,
+ buzz::CaptchaChallenge* captcha_challenge) {
+ ASSERT(client != NULL);
+ ASSERT(error && subcode && stream_error && captcha_challenge);
+
+ *error = client->GetError(subcode);
+ *captcha_challenge = client->GetCaptchaChallenge();
+
+ *stream_error = NULL;
+ if (*error == buzz::XmppEngine::ERROR_STREAM) {
+ const buzz::XmlElement* error_element = client->GetStreamError();
+ if (error_element) {
+ *stream_error = new buzz::XmlElement(*error_element);
+ }
+ }
+}
+
+SingleLoginAttempt::SingleLoginAttempt(talk_base::Task* parent,
+ LoginSettings* login_settings,
+ bool successful_connection)
+ : talk_base::Task(parent),
+ state_(buzz::XmppEngine::STATE_NONE),
+ code_(buzz::XmppEngine::ERROR_NONE),
+ subcode_(0),
+ need_authentication_(false),
+ certificate_expired_(false),
+ cookie_refreshed_(false),
+ successful_connection_(successful_connection),
+ login_settings_(login_settings),
+ client_(NULL) {
+ connection_generator_.reset(new XmppConnectionGenerator(
+ this,
+ &login_settings_->connection_options(),
+ login_settings_->proxy_only(),
+ login_settings_->server_list(),
+ login_settings_->server_count()));
+
+ connection_generator_->SignalExhaustedSettings.connect(
+ this,
+ &SingleLoginAttempt::OnAttemptedAllConnections);
+ connection_generator_->SignalNewSettings.connect(
+ this,
+ &SingleLoginAttempt::DoLogin);
+}
+
+SingleLoginAttempt::~SingleLoginAttempt() {
+ // If this assertion goes off, it means that "Stop()" didn't get
+ // called like it should have been.
+ ASSERT(client_ == NULL);
+}
+
+bool SingleLoginAttempt::auto_reconnect() const {
+ return login_settings_->connection_options().auto_reconnect();
+}
+
+const talk_base::ProxyInfo& SingleLoginAttempt::proxy() const {
+ ASSERT(connection_generator_.get());
+ return connection_generator_->proxy();
+}
+
+int SingleLoginAttempt::ProcessStart() {
+ ASSERT(GetState() == talk_base::Task::STATE_START);
+ connection_generator_->StartGenerating();
+
+ // After being started, this class is callback driven and does
+ // signaling from those callbacks (with checks to see if it is
+ // done if it may be called back from something that isn't a child task).
+ return talk_base::Task::STATE_BLOCKED;
+}
+
+void SingleLoginAttempt::Stop() {
+ ClearClient();
+ talk_base::Task::Stop();
+
+ // No more signals should happen after being stopped.
+ // (This is needed because some of these signals
+ // happen due to other components doing signaling which
+ // may continue running even though this task is stopped.)
+ SignalUnexpectedDisconnect.disconnect_all();
+ SignalRedirect.disconnect_all();
+ SignalLoginFailure.disconnect_all();
+ SignalNeedAutoReconnect.disconnect_all();
+ SignalClientStateChange.disconnect_all();
+}
+
+void SingleLoginAttempt::OnAttemptedAllConnections(
+ bool successfully_resolved_dns,
+ int first_dns_error) {
+
+ // Maybe we needed proxy authentication?
+ if (need_authentication_) {
+ LoginFailure failure(LoginFailure::PROXY_AUTHENTICATION_ERROR);
+ SignalLoginFailure(failure);
+ return;
+ }
+
+ if (certificate_expired_) {
+ LoginFailure failure(LoginFailure::CERTIFICATE_EXPIRED_ERROR);
+ SignalLoginFailure(failure);
+ return;
+ }
+
+ if (!successfully_resolved_dns) {
+ code_ = buzz::XmppEngine::ERROR_SOCKET;
+ subcode_ = first_dns_error;
+ }
+
+ LOG(INFO) << "Connection failed with error " << code_;
+
+ // We were connected and we had a problem
+ if (successful_connection_ && auto_reconnect()) {
+ SignalNeedAutoReconnect();
+ // expect to be deleted at this point
+ return;
+ }
+
+ DiagnoseConnectionError();
+}
+
+void SingleLoginAttempt::UseNextConnection() {
+ ASSERT(connection_generator_.get() != NULL);
+ ClearClient();
+ connection_generator_->UseNextConnection();
+}
+
+void SingleLoginAttempt::UseCurrentConnection() {
+ ASSERT(connection_generator_.get() != NULL);
+ ClearClient();
+ connection_generator_->UseCurrentConnection();
+}
+
+void SingleLoginAttempt::DoLogin(
+ const ConnectionSettings& connection_settings) {
+ if (client_) {
+ return;
+ }
+
+ buzz::XmppClientSettings client_settings;
+ // set the user settings portion
+ *static_cast<buzz::XmppClientSettings*>(&client_settings) =
+ login_settings_->user_settings();
+ // fill in the rest of the client settings
+ connection_settings.FillXmppClientSettings(&client_settings);
+
+ client_ = new buzz::XmppClient(this);
+ SignalLogInput.repeat(client_->SignalLogInput);
+ SignalLogOutput.repeat(client_->SignalLogOutput);
+
+ // listen for connection progress
+ client_->SignalStateChange.connect(this,
+ &SingleLoginAttempt::OnClientStateChange);
+
+ // transition to "start"
+ OnClientStateChange(buzz::XmppEngine::STATE_START);
+ // start connecting
+ client_->Connect(client_settings, login_settings_->lang(),
+ CreateSocket(client_settings),
+ CreatePreXmppAuth(client_settings));
+ client_->Start();
+}
+
+void SingleLoginAttempt::OnAuthenticationError() {
+ // We can check this flag later if all connection options fail
+ need_authentication_ = true;
+}
+
+void SingleLoginAttempt::OnCertificateExpired() {
+ // We can check this flag later if all connection options fail
+ certificate_expired_ = true;
+}
+
+
+buzz::AsyncSocket* SingleLoginAttempt::CreateSocket(
+ const buzz::XmppClientSettings& xcs) {
+ bool allow_unverified_certs =
+ login_settings_->connection_options().allow_unverified_certs();
+ XmppSocketAdapter* adapter = new XmppSocketAdapter(xcs,
+ allow_unverified_certs);
+ adapter->SignalAuthenticationError.connect(
+ this,
+ &SingleLoginAttempt::OnAuthenticationError);
+ if (login_settings_->firewall()) {
+ adapter->set_firewall(true);
+ }
+ return adapter;
+}
+
+buzz::PreXmppAuth* SingleLoginAttempt::CreatePreXmppAuth(
+ const buzz::XmppClientSettings& xcs) {
+ if (login_settings_->no_gaia_auth())
+ return NULL;
+
+ // For GMail, use Gaia preauthentication over HTTP
+ buzz::GaiaAuth* auth = new buzz::GaiaAuth(GetUserAgentString(),
+ GetProductSignature());
+ auth->SignalAuthenticationError.connect(
+ this,
+ &SingleLoginAttempt::OnAuthenticationError);
+ auth->SignalCertificateExpired.connect(
+ this,
+ &SingleLoginAttempt::OnCertificateExpired);
+ auth->SignalFreshAuthCookie.connect(
+ this,
+ &SingleLoginAttempt::OnFreshAuthCookie);
+ auth->set_token_service(xcs.token_service());
+
+ talk_base::ProxyInfo proxy;
+ FillProxyInfo(xcs, &proxy);
+ auth->set_proxy(proxy);
+ auth->set_firewall(login_settings_->firewall());
+ return auth;
+}
+
+void SingleLoginAttempt::OnFreshAuthCookie(const std::string& auth_cookie) {
+ // Remember this is a fresh cookie
+ cookie_refreshed_ = true;
+
+ // TODO(sync): do the cookie logic (part of which is in the #if 0 below)
+
+ // The following code is what PhoneWindow does for the equivalent method
+#if 0
+ // Save cookie
+ AccountInfo current(account_history_.current());
+ current.set_auth_cookie(auth_cookie);
+ account_history_.set_current(current);
+
+ // Calc next time to refresh cookie, between 5 and 10 days
+ // The cookie has 14 days of life; this gives at least 4 days of retries
+ // before the current cookie expires, maximizing the chance of
+ // having a valid cookie next time the connection servers go down.
+ FTULL now;
+
+ // NOTE: The following line is win32. Address this when implementing this
+ // code (doing "the cookie logic")
+ GetSystemTimeAsFileTime(&(now.ft));
+ ULONGLONG five_days = (ULONGLONG)10000 * 1000 * 60 * 60 * 24 * 5; // 5 days
+ ULONGLONG random = (ULONGLONG)10000 * // get to 100 ns units
+ ((rand() % (5 * 24 * 60)) * (60 * 1000) + // random min. in 5 day period
+ (rand() % 1000) * 60); // random 1/1000th of a minute
+ next_cookie_refresh_ = now.ull + five_days + random; // 5-10 days
+#endif
+}
+
+void SingleLoginAttempt::DiagnoseConnectionError() {
+ switch (code_) {
+ case buzz::XmppEngine::ERROR_MISSING_USERNAME:
+ case buzz::XmppEngine::ERROR_NETWORK_TIMEOUT:
+ case buzz::XmppEngine::ERROR_DOCUMENT_CLOSED:
+ case buzz::XmppEngine::ERROR_BIND:
+ case buzz::XmppEngine::ERROR_AUTH:
+ case buzz::XmppEngine::ERROR_TLS:
+ case buzz::XmppEngine::ERROR_UNAUTHORIZED:
+ case buzz::XmppEngine::ERROR_VERSION:
+ case buzz::XmppEngine::ERROR_STREAM:
+ case buzz::XmppEngine::ERROR_XML:
+ case buzz::XmppEngine::ERROR_NONE:
+ default: {
+ LoginFailure failure(LoginFailure::XMPP_ERROR, code_, subcode_);
+ SignalLoginFailure(failure);
+ return;
+ }
+
+ // The following errors require diagnosistics:
+ // * spurious close of connection
+ // * socket errors after auth
+ case buzz::XmppEngine::ERROR_CONNECTION_CLOSED:
+ case buzz::XmppEngine::ERROR_SOCKET:
+ break;
+ }
+
+ talk_base::AsyncHttpRequest *http_request =
+ new talk_base::AsyncHttpRequest(GetUserAgentString());
+ http_request->set_host("www.google.com");
+ http_request->set_port(80);
+ http_request->set_secure(false);
+ http_request->request().path = "/";
+ http_request->request().verb = talk_base::HV_GET;
+
+ talk_base::ProxyInfo proxy;
+ ASSERT(connection_generator_.get() != NULL);
+ if (connection_generator_.get()) {
+ proxy = connection_generator_->proxy();
+ }
+ http_request->set_proxy(proxy);
+ http_request->set_firewall(login_settings_->firewall());
+
+ http_request->SignalWorkDone.connect(this,
+ &SingleLoginAttempt::OnHttpTestDone);
+ http_request->Start();
+ http_request->Release();
+}
+
+void SingleLoginAttempt::OnHttpTestDone(talk_base::SignalThread* thread) {
+ ASSERT(thread != NULL);
+
+ talk_base::AsyncHttpRequest* request =
+ static_cast<talk_base::AsyncHttpRequest*>(thread);
+
+ if (request->response().scode == 200) {
+ // We were able to do an HTTP GET of www.google.com:80
+
+ //
+ // The original error should be reported
+ //
+ LoginFailure failure(LoginFailure::XMPP_ERROR, code_, subcode_);
+ SignalLoginFailure(failure);
+ return;
+ }
+
+ // Otherwise lets transmute the error into ERROR_SOCKET, and put
+ // the subcode as an indicator of what we think the problem
+ // might be.
+
+#if 0
+ // TODO(sync): determine if notifier has an analogous situation
+
+ //
+ // We weren't able to do an HTTP GET of www.google.com:80
+ //
+ GAutoupdater::Version version_logged_in(g_options.version_logged_in());
+ GAutoupdater::Version version_installed(GetProductVersion().c_str());
+ if (version_logged_in < version_installed) {
+ //
+ // Google Talk has been updated and can no longer connect
+ // to the Google Talk Service. Your firewall is probably
+ // not allowing the new version of Google Talk to connect
+ // to the internet. Please adjust your firewall settings
+ // to allow the new version of Google Talk to connect to
+ // the internet.
+ //
+ // We'll use the "error=1" to help figure this out for now
+ //
+ LoginFailure failure(LoginFailure::XMPP_ERROR,
+ buzz::XmppEngine::ERROR_SOCKET,
+ 1);
+ SignalLoginFailure(failure);
+ return;
+ }
+#endif
+
+ //
+ // Any other checking we can add here?
+ //
+
+ //
+ // Google Talk is unable to use your internet connection. Either your
+ // network isn't configured or Google Talk is being blocked by
+ // a local firewall.
+ //
+ // We'll use the "error=0" to help figure this out for now
+ //
+ LoginFailure failure(LoginFailure::XMPP_ERROR,
+ buzz::XmppEngine::ERROR_SOCKET,
+ 0);
+ SignalLoginFailure(failure);
+}
+
+void SingleLoginAttempt::OnClientStateChange(buzz::XmppEngine::State state) {
+ if (state_ == state)
+ return;
+
+ buzz::XmppEngine::State previous_state = state_;
+ state_ = state;
+
+ switch (state) {
+ case buzz::XmppEngine::STATE_NONE:
+ case buzz::XmppEngine::STATE_START:
+ case buzz::XmppEngine::STATE_OPENING:
+ // do nothing
+ break;
+ case buzz::XmppEngine::STATE_OPEN:
+ successful_connection_ = true;
+ break;
+ case buzz::XmppEngine::STATE_CLOSED:
+ OnClientStateChangeClosed(previous_state);
+ break;
+ }
+ SignalClientStateChange(state);
+ if (state_ == buzz::XmppEngine::STATE_CLOSED) {
+ OnClientStateChange(buzz::XmppEngine::STATE_NONE);
+ }
+}
+
+void SingleLoginAttempt::ClearClient() {
+ if (client_ != NULL) {
+ client_->Disconnect();
+
+ // If this assertion goes off, it means that the disconnect didn't occur
+ // properly. See SingleLoginAttempt::OnClientStateChange,
+ // case XmppEngine::STATE_CLOSED
+ ASSERT(client_ == NULL);
+ }
+}
+
+void SingleLoginAttempt::OnClientStateChangeClosed(
+ buzz::XmppEngine::State previous_state) {
+ buzz::XmppEngine::Error error = buzz::XmppEngine::ERROR_NONE;
+ int error_subcode = 0;
+ buzz::CaptchaChallenge captcha_challenge;
+ buzz::XmlElement* stream_error_ptr;
+ GetClientErrorInformation(client_,
+ &error,
+ &error_subcode,
+ &stream_error_ptr,
+ &captcha_challenge);
+ scoped_ptr<buzz::XmlElement> stream_error(stream_error_ptr);
+
+ client_->SignalStateChange.disconnect(this);
+ client_ = NULL;
+
+ if (error == buzz::XmppEngine::ERROR_NONE) {
+ SignalLogoff();
+ return;
+ } else if (previous_state == buzz::XmppEngine::STATE_OPEN) {
+ // Handler should attempt reconnect
+ SignalUnexpectedDisconnect();
+ return;
+ } else {
+ HandleConnectionError(error, error_subcode, stream_error.get(),
+ captcha_challenge);
+ }
+}
+
+void SingleLoginAttempt::HandleConnectionPasswordError(
+ const buzz::CaptchaChallenge& captcha_challenge) {
+ LOG(LS_VERBOSE) << "SingleLoginAttempt::HandleConnectionPasswordError";
+
+ // Clear the auth cookie
+ std::string current_auth_cookie =
+ login_settings_->user_settings().auth_cookie();
+ login_settings_->modifiable_user_settings()->set_auth_cookie("");
+ // If there was an auth cookie and it was the same as the last
+ // auth cookie, then it is a stale cookie. Retry login.
+ if (!current_auth_cookie.empty() && !cookie_refreshed_) {
+ UseCurrentConnection();
+ return;
+ }
+
+ LoginFailure failure(LoginFailure::XMPP_ERROR, code_, subcode_,
+ captcha_challenge);
+ SignalLoginFailure(failure);
+}
+
+void SingleLoginAttempt::HandleConnectionError(
+ buzz::XmppEngine::Error code,
+ int subcode,
+ const buzz::XmlElement* stream_error,
+ const buzz::CaptchaChallenge& captcha_challenge) {
+ LOG_F(LS_VERBOSE) << "(" << code << ", " << subcode << ")";
+
+ // Save off the error code information, so we can use it
+ // to tell the user what went wrong if all else fails
+ code_ = code;
+ subcode_ = subcode;
+ if ((code_ == buzz::XmppEngine::ERROR_UNAUTHORIZED) ||
+ (code_ == buzz::XmppEngine::ERROR_MISSING_USERNAME)) {
+ // There was a problem with credentials (username/password)
+ HandleConnectionPasswordError(captcha_challenge);
+ return;
+ }
+
+ // Unexpected disconnect,
+ // Unreachable host,
+ // Or internal server binding error -
+ // All these are temporary problems, so continue reconnecting
+
+ // GaiaAuth signals this directly via SignalCertificateExpired, but
+ // SChannelAdapter propagates the error through SocketWindow as a socket
+ // error.
+ if (code_ == buzz::XmppEngine::ERROR_SOCKET &&
+ subcode_ == SEC_E_CERT_EXPIRED) {
+ certificate_expired_ = true;
+ }
+
+ login_settings_->modifiable_user_settings()->set_resource("");
+
+ // Look for stream::error server redirection stanza "see-other-host"
+ if (stream_error) {
+ const buzz::XmlElement* other =
+ stream_error->FirstNamed(buzz::QN_XSTREAM_SEE_OTHER_HOST);
+ if (other) {
+ const buzz::XmlElement* text =
+ stream_error->FirstNamed(buzz::QN_XSTREAM_TEXT);
+ if (text) {
+ // Yep, its a "stream:error" with "see-other-host" text, let's
+ // parse out the server:port, and then reconnect with that.
+ const std::string& redirect = text->BodyText();
+ unsigned int colon = redirect.find(":");
+ int redirect_port = kDefaultXmppPort;
+ std::string redirect_server;
+ if (colon == std::string::npos) {
+ redirect_server = redirect;
+ } else {
+ redirect_server = redirect.substr(0, colon);
+ const std::string& port_text = redirect.substr(colon + 1);
+ std::istringstream ist(port_text);
+ ist >> redirect_port;
+ }
+ // we never allow a redirect to port 0
+ if (redirect_port == 0) {
+ redirect_port = kDefaultXmppPort;
+ }
+ SignalRedirect(redirect_server, redirect_port);
+ // may be deleted at this point
+ return;
+ }
+ }
+ }
+
+ ASSERT(connection_generator_.get() != NULL);
+ if (!connection_generator_.get()) {
+ return;
+ }
+
+ // Iterate to the next possible connection (still trying to connect)
+ UseNextConnection();
+}
+} // namespace notifier
diff --git a/chrome/browser/sync/notifier/communicator/single_login_attempt.h b/chrome/browser/sync/notifier/communicator/single_login_attempt.h
new file mode 100644
index 0000000..ec265ea
--- /dev/null
+++ b/chrome/browser/sync/notifier/communicator/single_login_attempt.h
@@ -0,0 +1,139 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_SYNC_NOTIFIER_COMMUNICATOR_SINGLE_LOGIN_ATTEMPT_H_
+#define CHROME_BROWSER_SYNC_NOTIFIER_COMMUNICATOR_SINGLE_LOGIN_ATTEMPT_H_
+#include <string>
+
+#include "chrome/browser/sync/notifier/communicator/login.h"
+#include "talk/base/scoped_ptr.h"
+#include "talk/base/sigslot.h"
+#include "talk/base/task.h"
+#include "talk/xmpp/xmppengine.h"
+
+namespace buzz {
+class AsyncSocket;
+class CaptchaChallenge;
+class PreXmppAuth;
+class XmppClient;
+class XmppClientSettings;
+class XmppClientSettings;
+}
+
+namespace talk_base {
+class FirewallManager;
+struct ProxyInfo;
+class SignalThread;
+class Task;
+}
+
+namespace notifier {
+class ConnectionSettings;
+class LoginFailure;
+class LoginSettings;
+struct ServerInformation;
+class XmppConnectionGenerator;
+
+// Handles all of the aspects of a single login attempt
+// (across multiple ip addresses) and maintainence. By containing
+// this within one class, when another login attempt is made,
+// this class will be disposed and all of the signalling for the
+// previous login attempt will be cleaned up immediately.
+//
+// This is a task to allow for cleaning this up when a signal
+// is being fired. Technically, delete this during the firing of
+// a signal could work but it is fragile.
+class SingleLoginAttempt : public talk_base::Task, public sigslot::has_slots<> {
+ public:
+ SingleLoginAttempt(talk_base::Task* parent,
+ LoginSettings* login_settings,
+ bool successful_connection);
+ ~SingleLoginAttempt();
+ virtual int ProcessStart();
+ void UseNextConnection();
+ void UseCurrentConnection();
+
+ buzz::XmppClient* xmpp_client() {
+ return client_;
+ }
+
+ // Returns the proxy that is being used to connect (or
+ // the default proxy information if all attempted
+ // connections failed).
+ const talk_base::ProxyInfo& proxy() const;
+
+ // Typically handled by creating a new SingleLoginAttempt
+ // and doing StartConnection
+ sigslot::signal0<> SignalUnexpectedDisconnect;
+
+ // Typically handled by setting storing the redirect for 5 seconds,
+ // and setting it into LoginSettings, then creating a new SingleLoginAttempt,
+ // and doing StartConnection.
+ //
+ // SignalRedirect(const std::string& redirect_server, int redirect_port);
+ sigslot::signal2<const std::string&, int> SignalRedirect;
+
+ sigslot::signal0<> SignalNeedAutoReconnect;
+
+ // SignalClientStateChange(buzz::XmppEngine::State new_state);
+ sigslot::signal1<buzz::XmppEngine::State> SignalClientStateChange;
+
+ // See the LoginFailure for how to handle this.
+ sigslot::signal1<const LoginFailure&> SignalLoginFailure;
+
+ // Sent when there is a graceful log-off (state goes to closed
+ // with no error)
+ sigslot::signal0<> SignalLogoff;
+
+ sigslot::repeater2<const char*, int> SignalLogInput;
+ sigslot::repeater2<const char*, int> SignalLogOutput;
+
+ protected:
+ virtual void Stop();
+
+ private:
+ void DoLogin(const ConnectionSettings& connection_settings);
+ buzz::AsyncSocket* CreateSocket(const buzz::XmppClientSettings& xcs);
+ buzz::PreXmppAuth* CreatePreXmppAuth(const buzz::XmppClientSettings& xcs);
+
+ // cleans up any xmpp client state to get ready for a new one
+ void ClearClient();
+
+ void HandleConnectionError(
+ buzz::XmppEngine::Error code,
+ int subcode,
+ const buzz::XmlElement* stream_error,
+ const buzz::CaptchaChallenge& captcha_challenge);
+ void HandleConnectionPasswordError(
+ const buzz::CaptchaChallenge& captcha_challenge);
+
+ void DiagnoseConnectionError();
+ void OnHttpTestDone(talk_base::SignalThread* thread);
+
+ void OnAuthenticationError();
+ void OnCertificateExpired();
+ void OnFreshAuthCookie(const std::string& auth_cookie);
+ void OnClientStateChange(buzz::XmppEngine::State state);
+ void OnClientStateChangeClosed(buzz::XmppEngine::State previous_state);
+ void OnAttemptedAllConnections(bool successfully_resolved_dns,
+ int first_dns_error);
+
+ bool auto_reconnect() const;
+
+ buzz::XmppEngine::State state_;
+ buzz::XmppEngine::Error code_;
+ int subcode_;
+ bool need_authentication_;
+ bool certificate_expired_;
+ bool cookie_refreshed_;
+ bool successful_connection_;
+ LoginSettings* login_settings_;
+ buzz::XmppClient* client_;
+ scoped_ptr<XmppConnectionGenerator> connection_generator_;
+
+ DISALLOW_COPY_AND_ASSIGN(SingleLoginAttempt);
+};
+} // namespace notifier
+
+#endif // CHROME_BROWSER_SYNC_NOTIFIER_COMMUNICATOR_SINGLE_LOGIN_ATTEMPT_H_
diff --git a/chrome/browser/sync/notifier/communicator/talk_auth_task.cc b/chrome/browser/sync/notifier/communicator/talk_auth_task.cc
new file mode 100644
index 0000000..a69609c7
--- /dev/null
+++ b/chrome/browser/sync/notifier/communicator/talk_auth_task.cc
@@ -0,0 +1,73 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/sync/notifier/communicator/talk_auth_task.h"
+
+#include "chrome/browser/sync/notifier/communicator/login.h"
+#include "chrome/browser/sync/notifier/communicator/login_settings.h"
+#include "chrome/browser/sync/notifier/communicator/product_info.h"
+#include "chrome/browser/sync/notifier/gaia_auth/gaiaauth.h"
+#include "talk/base/common.h"
+#include "talk/base/urlencode.h"
+#include "talk/xmpp/xmppclient.h"
+
+namespace notifier {
+const char kTalkGadgetAuthPath[] = "/auth";
+
+TalkAuthTask::TalkAuthTask(talk_base::Task* parent,
+ Login* login,
+ const char* url)
+ : talk_base::Task(parent),
+ login_(login),
+ url_(url) {
+ ASSERT(login && !url_.empty());
+}
+
+int TalkAuthTask::ProcessStart() {
+ auth_.reset(new buzz::GaiaAuth(GetUserAgentString(),
+ GetProductSignature()));
+ auth_->SignalAuthDone.connect(
+ this,
+ &TalkAuthTask::OnAuthDone);
+ auth_->StartAuth(login_->xmpp_client()->jid().BareJid(),
+ login_->login_settings().user_settings().pass(),
+ "talk");
+ return STATE_RESPONSE;
+}
+
+int TalkAuthTask::ProcessResponse() {
+ ASSERT(auth_.get());
+ if (!auth_->IsAuthDone()) {
+ return STATE_BLOCKED;
+ }
+ SignalAuthDone(*this);
+ return STATE_DONE;
+}
+
+
+void TalkAuthTask::OnAuthDone() {
+ Wake();
+}
+
+bool TalkAuthTask::HadError() const {
+ return auth_->HadError();
+}
+
+std::string TalkAuthTask::GetAuthenticatedUrl(
+ const char* talk_base_url) const {
+ ASSERT(talk_base_url && *talk_base_url && !auth_->HadError());
+
+ std::string auth_url(talk_base_url);
+ auth_url.append(kTalkGadgetAuthPath);
+ auth_url.append("?silent=true&redirect=true&host=");
+ auth_url.append(UrlEncodeString(url_));
+ auth_url.append("&auth=");
+ auth_url.append(auth_->GetAuth());
+ return auth_url;
+}
+
+std::string TalkAuthTask::GetSID() const {
+ return auth_->GetSID();
+}
+} // namespace notifier
diff --git a/chrome/browser/sync/notifier/communicator/talk_auth_task.h b/chrome/browser/sync/notifier/communicator/talk_auth_task.h
new file mode 100644
index 0000000..2f690a37
--- /dev/null
+++ b/chrome/browser/sync/notifier/communicator/talk_auth_task.h
@@ -0,0 +1,62 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_SYNC_NOTIFIER_COMMUNICATOR_TALK_AUTH_TASK_H_
+#define CHROME_BROWSER_SYNC_NOTIFIER_COMMUNICATOR_TALK_AUTH_TASK_H_
+
+#include "talk/base/scoped_ptr.h"
+#include "talk/base/sigslot.h"
+#include "talk/base/task.h"
+
+namespace buzz {
+class GaiaAuth;
+}
+
+namespace notifier {
+class Login;
+
+// Create an authenticated talk url from an unauthenticated url
+class TalkAuthTask : public talk_base::Task, public sigslot::has_slots<> {
+ public:
+ TalkAuthTask(talk_base::Task* parent,
+ Login* login,
+ const char* url);
+
+ // An abort method which doesn't take any parameters.
+ // (talk_base::Task::Abort() has a default parameter.)
+ //
+ // The primary purpose of this method is to allow a
+ // signal to be hooked up to abort this task.
+ void Abort() {
+ talk_base::Task::Abort();
+ }
+
+ const std::string& url() {
+ return url_;
+ }
+
+ std::string GetAuthenticatedUrl(const char* talk_base_url) const;
+ std::string GetSID() const;
+
+ sigslot::signal1<const TalkAuthTask&> SignalAuthDone;
+
+ bool HadError() const;
+
+ // TODO(sync): add captcha support
+
+ protected:
+ virtual int ProcessStart();
+ virtual int ProcessResponse();
+
+ private:
+ void OnAuthDone();
+
+ scoped_ptr<buzz::GaiaAuth> auth_;
+ Login* login_;
+ std::string url_;
+ DISALLOW_COPY_AND_ASSIGN(TalkAuthTask);
+};
+} // namespace notifier
+
+#endif // CHROME_BROWSER_SYNC_NOTIFIER_COMMUNICATOR_TALK_AUTH_TASK_H_
diff --git a/chrome/browser/sync/notifier/communicator/xml_parse_helpers-inl.h b/chrome/browser/sync/notifier/communicator/xml_parse_helpers-inl.h
new file mode 100644
index 0000000..b400218
--- /dev/null
+++ b/chrome/browser/sync/notifier/communicator/xml_parse_helpers-inl.h
@@ -0,0 +1,24 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_SYNC_NOTIFIER_COMMUNICATOR_XML_PARSE_HELPERS_INL_H_
+#define CHROME_BROWSER_SYNC_NOTIFIER_COMMUNICATOR_XML_PARSE_HELPERS_INL_H_
+
+#include <sstream>
+
+#include "chrome/browser/sync/notifier/communicator/xml_parse_helpers.h"
+#include "talk/xmllite/xmlelement.h"
+
+namespace notifier {
+
+template<class T>
+void SetAttr(buzz::XmlElement* xml, const buzz::QName& name, const T& data) {
+ std::ostringstream ost;
+ ost << data;
+ xml->SetAttr(name, ost.str());
+}
+
+} // namespace notifier
+
+#endif // CHROME_BROWSER_SYNC_NOTIFIER_COMMUNICATOR_XML_PARSE_HELPERS_INL_H_
diff --git a/chrome/browser/sync/notifier/communicator/xml_parse_helpers.cc b/chrome/browser/sync/notifier/communicator/xml_parse_helpers.cc
new file mode 100644
index 0000000..b05f439
--- /dev/null
+++ b/chrome/browser/sync/notifier/communicator/xml_parse_helpers.cc
@@ -0,0 +1,185 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/sync/notifier/communicator/xml_parse_helpers.h"
+#include "chrome/browser/sync/notifier/communicator/xml_parse_helpers-inl.h"
+
+#include <string>
+
+#include "chrome/browser/sync/notifier/base/string.h"
+#include "talk/base/basicdefs.h"
+#include "talk/base/stream.h"
+#include "talk/xmllite/xmlbuilder.h"
+#include "talk/xmllite/xmlelement.h"
+#include "talk/xmllite/xmlparser.h"
+#include "talk/xmllite/xmlprinter.h"
+#include "talk/xmpp/jid.h"
+
+namespace notifier {
+
+buzz::XmlElement* ReadXmlFromStream(talk_base::StreamInterface* stream) {
+ buzz::XmlBuilder builder;
+ buzz::XmlParser parser(&builder);
+
+ const int kBufferSize = 4 * 1024;
+ char buf[kBufferSize];
+
+ talk_base::StreamResult result = talk_base::SR_SUCCESS;
+ while(true) {
+ size_t read = 0;
+
+ // Read a chunk
+ result = stream->Read(buf, kBufferSize, &read, NULL);
+ if (result != talk_base::SR_SUCCESS)
+ break;
+
+ // Pass it to the parser
+ parser.Parse(buf, read, false);
+ }
+
+ if (result == talk_base::SR_EOS) {
+ parser.Parse(NULL, 0, true);
+ return builder.CreateElement();
+ }
+
+ return NULL;
+}
+
+bool ParseInt64Attr(const buzz::XmlElement* element,
+ const buzz::QName& attribute, int64* result) {
+ if (!element->HasAttr(attribute))
+ return false;
+ std::string text = element->Attr(attribute);
+ char* error = NULL;
+#ifdef POSIX
+ *result = atoll(text.c_str());
+#else
+ *result = _strtoi64(text.c_str(), &error, 10);
+#endif
+ return text.c_str() != error;
+}
+
+bool ParseIntAttr(const buzz::XmlElement* element, const buzz::QName& attribute,
+ int* result) {
+ if (!element->HasAttr(attribute))
+ return false;
+ std::string text = element->Attr(attribute);
+ char* error = NULL;
+ *result = static_cast<int>(strtol(text.c_str(), &error, 10));
+ return text.c_str() != error;
+}
+
+bool ParseBoolAttr(const buzz::XmlElement* element,
+ const buzz::QName& attribute, bool* result) {
+ int int_value = 0;
+ if (!ParseIntAttr(element, attribute, &int_value))
+ return false;
+ *result = int_value != 0;
+ return true;
+}
+
+bool ParseStringAttr(const buzz::XmlElement* element,
+ const buzz::QName& attribute, std::string* result) {
+ if (!element->HasAttr(attribute))
+ return false;
+ *result = element->Attr(attribute);
+ return true;
+}
+
+void WriteXmlToStream(talk_base::StreamInterface* stream,
+ const buzz::XmlElement* xml) {
+ // Save it all to a string and then write that string out to disk.
+ //
+ // This is probably really inefficient in multiple ways. We probably
+ // have an entire string copy of the XML in memory twice -- once in the
+ // stream and once in the string. There is probably a way to get the data
+ // directly out of the stream but I don't have the time to decode the stream
+ // classes right now.
+ std::ostringstream s;
+ buzz::XmlPrinter::PrintXml(&s, xml);
+ std::string output_string = s.str();
+ stream->WriteAll(output_string.data(), output_string.length(), NULL, NULL);
+}
+
+bool SetInt64Attr(buzz::XmlElement* element, const buzz::QName& attribute,
+ int64 value) {
+ if (!element->HasAttr(attribute))
+ return false;
+ element->AddAttr(attribute, Int64ToString(value).c_str());
+ return true;
+}
+
+bool SetIntAttr(buzz::XmlElement* element, const buzz::QName& attribute,
+ int value) {
+ if (!element->HasAttr(attribute))
+ return false;
+ element->AddAttr(attribute, IntToString(value).c_str());
+ return true;
+}
+
+bool SetBoolAttr(buzz::XmlElement* element, const buzz::QName& attribute,
+ bool value) {
+ int int_value = 0;
+ if (value) {
+ int_value = 1;
+ }
+ return SetIntAttr(element, attribute, int_value);
+}
+
+bool SetStringAttr(buzz::XmlElement* element, const buzz::QName& attribute,
+ const std::string& value) {
+ if (!element->HasAttr(attribute))
+ return false;
+ element->AddAttr(attribute, value);
+ return true;
+}
+
+
+// XmlStream
+
+XmlStream::XmlStream()
+ : state_(talk_base::SS_OPEN),
+ builder_(new buzz::XmlBuilder()),
+ parser_(new buzz::XmlParser(builder_.get())) {
+}
+
+XmlStream::~XmlStream() {
+}
+
+buzz::XmlElement* XmlStream::CreateElement() {
+ if (talk_base::SS_OPEN == state_) {
+ Close();
+ }
+ return builder_->CreateElement();
+}
+
+talk_base::StreamResult XmlStream::Read(void* buffer, size_t buffer_len,
+ size_t* read, int* error) {
+ if (error)
+ *error = -1;
+ return talk_base::SR_ERROR;
+}
+
+talk_base::StreamResult XmlStream::Write(const void* data, size_t data_len,
+ size_t* written, int* error) {
+ if (talk_base::SS_OPEN != state_) {
+ if (error)
+ *error = -1;
+ return talk_base::SR_ERROR;
+ }
+ parser_->Parse(static_cast<const char*>(data), data_len, false);
+ if (written)
+ *written = data_len;
+ return talk_base::SR_SUCCESS;
+}
+
+void XmlStream::Close() {
+ if (talk_base::SS_OPEN != state_)
+ return;
+
+ parser_->Parse(NULL, 0, true);
+ state_ = talk_base::SS_CLOSED;
+}
+
+} // namespace buzz
diff --git a/chrome/browser/sync/notifier/communicator/xml_parse_helpers.h b/chrome/browser/sync/notifier/communicator/xml_parse_helpers.h
new file mode 100644
index 0000000..0c918bd
--- /dev/null
+++ b/chrome/browser/sync/notifier/communicator/xml_parse_helpers.h
@@ -0,0 +1,75 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_SYNC_NOTIFIER_COMMUNICATOR_XML_PARSE_HELPERS_H_
+#define CHROME_BROWSER_SYNC_NOTIFIER_COMMUNICATOR_XML_PARSE_HELPERS_H_
+
+#include <string>
+
+#include "talk/base/basictypes.h"
+#include "talk/base/scoped_ptr.h"
+#include "talk/base/stream.h"
+
+namespace buzz {
+class XmlBuilder;
+class XmlElement;
+class XmlParser;
+class QName;
+}
+
+namespace notifier {
+buzz::XmlElement* ReadXmlFromStream(talk_base::StreamInterface* stream);
+bool ParseInt64Attr(const buzz::XmlElement* element,
+ const buzz::QName& attribute, int64* result);
+bool ParseIntAttr(const buzz::XmlElement* element,
+ const buzz::QName& attribute, int* result);
+bool ParseBoolAttr(const buzz::XmlElement* element,
+ const buzz::QName& attribute, bool* result);
+bool ParseStringAttr(const buzz::XmlElement* element,
+ const buzz::QName& attribute, std::string* result);
+
+void WriteXmlToStream(talk_base::StreamInterface* stream,
+ const buzz::XmlElement* xml);
+bool SetInt64Attr(buzz::XmlElement* element, const buzz::QName& attribute,
+ int64 result);
+bool SetIntAttr(buzz::XmlElement* element, const buzz::QName& attribute,
+ int result);
+bool SetBoolAttr(buzz::XmlElement* element, const buzz::QName& attribute,
+ bool result);
+bool SetStringAttr(buzz::XmlElement* element, const buzz::QName& attribute,
+ const std::string& result);
+
+template<class T>
+void SetAttr(buzz::XmlElement* xml, const buzz::QName& name, const T& data);
+
+///////////////////////////////////////////////////////////////////////////////
+// XmlStream
+///////////////////////////////////////////////////////////////////////////////
+
+class XmlStream : public talk_base::StreamInterface {
+ public:
+ XmlStream();
+ virtual ~XmlStream();
+
+ buzz::XmlElement* CreateElement();
+
+ virtual talk_base::StreamState GetState() const { return state_; }
+
+ virtual talk_base::StreamResult Read(void* buffer, size_t buffer_len,
+ size_t* read, int* error);
+ virtual talk_base::StreamResult Write(const void* data, size_t data_len,
+ size_t* written, int* error);
+ virtual void Close();
+
+ private:
+ talk_base::StreamState state_;
+ scoped_ptr<buzz::XmlBuilder> builder_;
+ scoped_ptr<buzz::XmlParser> parser_;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+} // namespace buzz
+
+#endif // CHROME_BROWSER_SYNC_NOTIFIER_COMMUNICATOR_XML_PARSE_HELPERS_H_
diff --git a/chrome/browser/sync/notifier/communicator/xmpp_connection_generator.cc b/chrome/browser/sync/notifier/communicator/xmpp_connection_generator.cc
new file mode 100644
index 0000000..f3a5f4c
--- /dev/null
+++ b/chrome/browser/sync/notifier/communicator/xmpp_connection_generator.cc
@@ -0,0 +1,210 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// XmppConnectionGenerator does the following algorithm:
+// proxy = ResolveProxyInformation(connection_options)
+// for server in server_list
+// get dns_addresses for server
+// connection_list = (dns_addresses X connection methods X proxy).shuffle()
+// for connection in connection_list
+// yield connection
+
+#include "chrome/browser/sync/notifier/communicator/xmpp_connection_generator.h"
+
+#include <vector>
+
+#include "chrome/browser/sync/notifier/base/async_dns_lookup.h"
+#include "chrome/browser/sync/notifier/base/signal_thread_task.h"
+#include "chrome/browser/sync/notifier/communicator/connection_options.h"
+#include "chrome/browser/sync/notifier/communicator/connection_settings.h"
+#include "chrome/browser/sync/notifier/communicator/product_info.h"
+#include "talk/base/autodetectproxy.h"
+#include "talk/base/httpcommon.h"
+#include "talk/base/logging.h"
+#include "talk/base/task.h"
+#include "talk/base/thread.h"
+#include "talk/xmpp/prexmppauth.h"
+#include "talk/xmpp/xmppclientsettings.h"
+#include "talk/xmpp/xmppengine.h"
+
+namespace notifier {
+
+XmppConnectionGenerator::XmppConnectionGenerator(
+ talk_base::Task* parent,
+ const ConnectionOptions* options,
+ bool proxy_only,
+ const ServerInformation* server_list,
+ int server_count)
+ : settings_list_(new ConnectionSettingsList()),
+ settings_index_(0),
+ server_list_(new ServerInformation[server_count]),
+ server_count_(server_count),
+ server_index_(-1),
+ proxy_only_(proxy_only),
+ successfully_resolved_dns_(false),
+ first_dns_error_(0),
+ options_(options),
+ parent_(parent) {
+ assert(parent);
+ assert(options);
+ assert(server_count_ > 0);
+ for (int i = 0; i < server_count_; ++i) {
+ server_list_[i] = server_list[i];
+ }
+}
+
+XmppConnectionGenerator::~XmppConnectionGenerator() {
+ LOG(LS_VERBOSE) << "XmppConnectionGenerator::~XmppConnectionGenerator";
+}
+
+const talk_base::ProxyInfo& XmppConnectionGenerator::proxy() const {
+ assert(settings_list_.get());
+ if (settings_index_ >= settings_list_->GetCount()) {
+ return settings_list_->proxy();
+ }
+
+ ConnectionSettings* settings = settings_list_->GetSettings(settings_index_);
+ return settings->proxy();
+}
+
+// Starts resolving proxy information
+void XmppConnectionGenerator::StartGenerating() {
+ LOG(LS_VERBOSE) << "XmppConnectionGenerator::StartGenerating";
+
+ talk_base::AutoDetectProxy* proxy_detect =
+ new talk_base::AutoDetectProxy(GetUserAgentString());
+
+ if (options_->autodetect_proxy()) {
+ // Pretend the xmpp server is https, when detecting whether a proxy is
+ // required to connect.
+ talk_base::Url<char> host_url("/",
+ server_list_[0].server.IPAsString().c_str(),
+ server_list_[0].server.port());
+ host_url.set_secure(true);
+ proxy_detect->set_server_url(host_url.url());
+ } else if (options_->proxy_host().length()) {
+ talk_base::SocketAddress proxy(options_->proxy_host(),
+ options_->proxy_port());
+ proxy_detect->set_proxy(proxy);
+ }
+ proxy_detect->set_auth_info(options_->use_proxy_auth(),
+ options_->auth_user(),
+ talk_base::CryptString(options_->auth_pass()));
+
+ SignalThreadTask<talk_base::AutoDetectProxy>* wrapper_task =
+ new SignalThreadTask<talk_base::AutoDetectProxy>(parent_, &proxy_detect);
+ wrapper_task->SignalWorkDone.connect(
+ this,
+ &XmppConnectionGenerator::OnProxyDetect);
+ wrapper_task->Start();
+}
+
+void XmppConnectionGenerator::OnProxyDetect(
+ talk_base::AutoDetectProxy* proxy_detect) {
+ LOG(LS_VERBOSE) << "XmppConnectionGenerator::OnProxyDetect";
+
+ ASSERT(settings_list_.get());
+ ASSERT(proxy_detect);
+ settings_list_->SetProxy(proxy_detect->proxy());
+
+ // Start iterating through the connections (which
+ // are generated on demand).
+ UseNextConnection();
+}
+
+void XmppConnectionGenerator::UseNextConnection() {
+ // Trying to connect
+
+ // Iterate to the next possible connection
+ settings_index_++;
+ if (settings_index_ < settings_list_->GetCount()) {
+ // We have more connection settings in the settings_list_ to try, kick
+ // off the next one.
+ UseCurrentConnection();
+ return;
+ }
+
+ // Iterate to the next possible server
+ server_index_++;
+ if (server_index_ < server_count_) {
+ AsyncDNSLookup* dns_lookup = new AsyncDNSLookup(
+ server_list_[server_index_].server);
+ SignalThreadTask<AsyncDNSLookup>* wrapper_task =
+ new SignalThreadTask<AsyncDNSLookup>(parent_, &dns_lookup);
+ wrapper_task->SignalWorkDone.connect(
+ this,
+ &XmppConnectionGenerator::OnServerDNSResolved);
+ wrapper_task->Start();
+ return;
+ }
+
+ // All out of possibilities
+ HandleExhaustedConnections();
+}
+
+void XmppConnectionGenerator::OnServerDNSResolved(
+ AsyncDNSLookup* dns_lookup) {
+ LOG(LS_VERBOSE) << "XmppConnectionGenerator::OnServerDNSResolved";
+
+ // Print logging info
+ LOG(LS_VERBOSE) << " server: " <<
+ server_list_[server_index_].server.ToString() <<
+ " error: " << dns_lookup->error();
+ if (first_dns_error_ == 0 && dns_lookup->error() != 0) {
+ first_dns_error_ = dns_lookup->error();
+ }
+
+ if (!successfully_resolved_dns_ && dns_lookup->ip_list().size() > 0) {
+ successfully_resolved_dns_ = true;
+ }
+
+ for (int i = 0; i < static_cast<int>(dns_lookup->ip_list().size()); ++i) {
+ LOG(LS_VERBOSE)
+ << " ip " << i << " : "
+ << talk_base::SocketAddress::IPToString(dns_lookup->ip_list()[i]);
+ }
+
+ // Build the ip list
+ assert(settings_list_.get());
+ settings_index_ = -1;
+ settings_list_->ClearPermutations();
+ settings_list_->AddPermutations(
+ server_list_[server_index_].server.IPAsString(),
+ dns_lookup->ip_list(),
+ server_list_[server_index_].server.port(),
+ server_list_[server_index_].special_port_magic,
+ proxy_only_);
+
+ UseNextConnection();
+}
+
+static const char * const PROTO_NAMES[cricket::PROTO_LAST+1] = {
+ "udp", "tcp", "ssltcp"
+};
+
+static const char* ProtocolToString(cricket::ProtocolType proto) {
+ return PROTO_NAMES[proto];
+}
+
+void XmppConnectionGenerator::UseCurrentConnection() {
+ LOG(LS_VERBOSE) << "XmppConnectionGenerator::UseCurrentConnection";
+
+ ConnectionSettings* settings = settings_list_->GetSettings(settings_index_);
+ LOG(LS_INFO) << "*** Attempting "
+ << ProtocolToString(settings->protocol()) << " connection to "
+ << settings->server().IPAsString() << ":"
+ << settings->server().port()
+ << " (via " << ProxyToString(settings->proxy().type)
+ << " proxy @ " << settings->proxy().address.IPAsString() << ":"
+ << settings->proxy().address.port() << ")";
+
+ SignalNewSettings(*settings);
+}
+
+void XmppConnectionGenerator::HandleExhaustedConnections() {
+ LOG_F(LS_VERBOSE) << "(" << buzz::XmppEngine::ERROR_SOCKET
+ << ", " << first_dns_error_ << ")";
+ SignalExhaustedSettings(successfully_resolved_dns_, first_dns_error_);
+}
+} // namespace notifier
diff --git a/chrome/browser/sync/notifier/communicator/xmpp_connection_generator.h b/chrome/browser/sync/notifier/communicator/xmpp_connection_generator.h
new file mode 100644
index 0000000..03a7a8f
--- /dev/null
+++ b/chrome/browser/sync/notifier/communicator/xmpp_connection_generator.h
@@ -0,0 +1,81 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_SYNC_NOTIFIER_COMMUNICATOR_XMPP_CONNECTION_GENERATOR_H_
+#define CHROME_BROWSER_SYNC_NOTIFIER_COMMUNICATOR_XMPP_CONNECTION_GENERATOR_H_
+#include <vector>
+
+#include "talk/base/scoped_ptr.h"
+#include "talk/base/sigslot.h"
+#include "talk/base/socketaddress.h"
+
+namespace talk_base {
+class AutoDetectProxy;
+struct ProxyInfo;
+class SignalThread;
+class Task;
+}
+
+namespace notifier {
+class AsyncDNSLookup;
+class ConnectionOptions;
+class ConnectionSettings;
+class ConnectionSettingsList;
+
+struct ServerInformation {
+ talk_base::SocketAddress server;
+ bool special_port_magic;
+};
+
+// Resolves dns names and iterates through the various ip address
+// and transport combinations.
+class XmppConnectionGenerator : public sigslot::has_slots<> {
+ public:
+ // parent is the parent for any tasks needed during this operation
+ // proxy_only indicates if true connections are only attempted using the proxy
+ // server_list is the list of connections to attempt in priority order
+ // server_count is the number of items in the server list
+ XmppConnectionGenerator(talk_base::Task* parent,
+ const ConnectionOptions* options,
+ bool proxy_only,
+ const ServerInformation* server_list,
+ int server_count);
+ ~XmppConnectionGenerator();
+
+ // Only call this once. Create a new XmppConnectionGenerator and
+ // delete the current one if the process needs to start again.
+ void StartGenerating();
+
+ void UseNextConnection();
+ void UseCurrentConnection();
+
+ const talk_base::ProxyInfo& proxy() const;
+
+ sigslot::signal1<const ConnectionSettings&> SignalNewSettings;
+
+ // SignalExhaustedSettings(bool successfully_resolved_dns,
+ // int first_dns_error);
+ sigslot::signal2<bool, int> SignalExhaustedSettings;
+
+ private:
+ void OnProxyDetect(talk_base::AutoDetectProxy* proxy_detect);
+ void OnServerDNSResolved(AsyncDNSLookup* dns_lookup);
+ void HandleExhaustedConnections();
+
+ talk_base::scoped_ptr<ConnectionSettingsList> settings_list_;
+ int settings_index_; // the setting that is currently being used
+ talk_base::scoped_array<ServerInformation> server_list_;
+ int server_count_;
+ int server_index_; // the server that is current being used
+ bool proxy_only_;
+ bool successfully_resolved_dns_;
+ int first_dns_error_;
+ const ConnectionOptions* options_;
+
+ talk_base::Task* parent_;
+ DISALLOW_COPY_AND_ASSIGN(XmppConnectionGenerator);
+};
+} // namespace notifier
+
+#endif // CHROME_BROWSER_SYNC_NOTIFIER_COMMUNICATOR_XMPP_CONNECTION_GENERATOR_H_
diff --git a/chrome/browser/sync/notifier/communicator/xmpp_log.cc b/chrome/browser/sync/notifier/communicator/xmpp_log.cc
new file mode 100644
index 0000000..30b0036
--- /dev/null
+++ b/chrome/browser/sync/notifier/communicator/xmpp_log.cc
@@ -0,0 +1,111 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#if LOGGING
+
+#include "chrome/browser/sync/notifier/communicator/xmpp_log.h"
+
+#include <iomanip>
+#include <string>
+#include <vector>
+
+#include "chrome/browser/sync/notifier/base/time.h"
+#include "talk/base/common.h"
+#include "talk/base/logging.h"
+
+namespace notifier {
+
+static bool IsAuthTag(const char* str, size_t len) {
+ // Beware that str is not NULL terminated
+ if (str[0] == '<' &&
+ str[1] == 'a' &&
+ str[2] == 'u' &&
+ str[3] == 't' &&
+ str[4] == 'h' &&
+ str[5] <= ' ') {
+ std::string tag(str, len);
+ if (tag.find("mechanism") != std::string::npos)
+ return true;
+ }
+ return false;
+}
+
+static bool IsChatText(const char* str, size_t len) {
+ // Beware that str is not NULL terminated
+ if (str[0] == '<' &&
+ str[1] == 'm' &&
+ str[2] == 'e' &&
+ str[3] == 's' &&
+ str[4] == 's' &&
+ str[5] == 'a' &&
+ str[6] == 'g' &&
+ str[7] == 'e' &&
+ str[8] <= ' ') {
+ std::string tag(str, len);
+ if (tag.find("chat") != std::string::npos)
+ return true;
+ }
+ return false;
+}
+
+void XmppLog::XmppPrint(bool output) {
+ std::vector<char>* buffer = output ?
+ &xmpp_output_buffer_ : &xmpp_input_buffer_;
+ const bool log_chat = LOG_CHECK_LEVEL(LS_SENSITIVE);
+ if (buffer->size()) {
+ char* time_string = GetLocalTimeAsString();
+ LOG(INFO) << (output ? "SEND >>>>>>>>>>>>>>>>>>>>>>>>>" :
+ "RECV <<<<<<<<<<<<<<<<<<<<<<<<<")
+ << " : " << time_string;
+
+ int start = 0;
+ int nest = 3;
+ for (int i = 0; i < static_cast<int>(buffer->size()); ++i) {
+ if ((*buffer)[i] == '>') {
+ bool indent;
+ if ((i > 0) && ((*buffer)[i - 1] == '/')) {
+ indent = false;
+ } else if ((start + 1 < static_cast<int>(buffer->size())) &&
+ ((*buffer)[start + 1] == '/')) {
+ indent = false;
+ nest -= 2;
+ } else {
+ indent = true;
+ }
+
+ // Output a tag
+ LOG(INFO) << std::setw(nest) << " "
+ << std::string(&((*buffer)[start]), i + 1 - start);
+
+ if (indent)
+ nest += 2;
+
+ // Note if it's a PLAIN auth tag
+ if (IsAuthTag(&((*buffer)[start]), i + 1 - start)) {
+ censor_password_ = true;
+ } else if (!log_chat && IsChatText(&((*buffer)[start]),
+ i + 1 - start)) {
+ censor_password_ = true;
+ }
+
+ start = i + 1;
+ }
+
+ if ((*buffer)[i] == '<' && start < i) {
+ if (censor_password_) {
+ LOG(INFO) << std::setw(nest) << " " << "## TEXT REMOVED ##";
+ censor_password_ = false;
+ } else {
+ LOG(INFO) << std::setw(nest) << " "
+ << std::string(&((*buffer)[start]), i - start);
+ }
+ start = i;
+ }
+ }
+ buffer->erase(buffer->begin(), buffer->begin() + start);
+ }
+}
+} // namespace notifier
+
+#endif // if LOGGING
diff --git a/chrome/browser/sync/notifier/communicator/xmpp_log.h b/chrome/browser/sync/notifier/communicator/xmpp_log.h
new file mode 100644
index 0000000..a6d12bd
--- /dev/null
+++ b/chrome/browser/sync/notifier/communicator/xmpp_log.h
@@ -0,0 +1,45 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_SYNC_NOTIFIER_COMMUNICATOR_XMPP_LOG_H_
+#define CHROME_BROWSER_SYNC_NOTIFIER_COMMUNICATOR_XMPP_LOG_H_
+
+#if LOGGING
+
+#include <vector>
+
+#include "talk/base/basictypes.h"
+#include "talk/base/sigslot.h"
+
+namespace notifier {
+
+// Log the xmpp input and output.
+class XmppLog : public sigslot::has_slots<> {
+ public:
+ XmppLog() : censor_password_(false) {
+ }
+
+ void Input(const char* data, int len) {
+ xmpp_input_buffer_.insert(xmpp_input_buffer_.end(), data, data + len);
+ XmppPrint(false);
+ }
+
+ void Output(const char* data, int len) {
+ xmpp_output_buffer_.insert(xmpp_output_buffer_.end(), data, data + len);
+ XmppPrint(true);
+ }
+
+ private:
+ void XmppPrint(bool output);
+
+ std::vector<char> xmpp_input_buffer_;
+ std::vector<char> xmpp_output_buffer_;
+ bool censor_password_;
+ DISALLOW_COPY_AND_ASSIGN(XmppLog);
+};
+} // namespace notifier
+
+#endif // if LOGGING
+
+#endif // CHROME_BROWSER_SYNC_NOTIFIER_COMMUNICATOR_XMPP_LOG_H_
diff --git a/chrome/browser/sync/notifier/communicator/xmpp_socket_adapter.cc b/chrome/browser/sync/notifier/communicator/xmpp_socket_adapter.cc
new file mode 100644
index 0000000..9bd65db
--- /dev/null
+++ b/chrome/browser/sync/notifier/communicator/xmpp_socket_adapter.cc
@@ -0,0 +1,437 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/sync/notifier/communicator/xmpp_socket_adapter.h"
+
+#include <iomanip>
+#include <string>
+
+#include "chrome/browser/sync/notifier/communicator/product_info.h"
+#include "talk/base/byteorder.h"
+#include "talk/base/common.h"
+#include "talk/base/firewallsocketserver.h"
+#include "talk/base/logging.h"
+#include "talk/base/socketadapters.h"
+#include "talk/base/ssladapter.h"
+#include "talk/xmpp/xmppengine.h"
+
+namespace notifier {
+
+XmppSocketAdapter::XmppSocketAdapter(const buzz::XmppClientSettings& xcs,
+ bool allow_unverified_certs)
+ : state_(STATE_CLOSED),
+ error_(ERROR_NONE),
+ wsa_error_(0),
+ socket_(NULL),
+ protocol_(xcs.protocol()),
+ firewall_(false),
+ write_buffer_(NULL),
+ write_buffer_length_(0),
+ write_buffer_capacity_(0),
+ allow_unverified_certs_(allow_unverified_certs) {
+ proxy_.type = xcs.proxy();
+ proxy_.address.SetIP(xcs.proxy_host(), false);
+ proxy_.address.SetPort(xcs.proxy_port());
+ proxy_.username = xcs.proxy_user();
+ proxy_.password = xcs.proxy_pass();
+}
+
+XmppSocketAdapter::~XmppSocketAdapter() {
+ FreeState();
+
+ // Clean up any previous socket - cannot delete socket on close because
+ // close happens during the child socket's stack callback.
+ if (socket_) {
+ delete socket_;
+ socket_ = NULL;
+ }
+}
+
+bool XmppSocketAdapter::FreeState() {
+ int code = 0;
+
+ // Clean up the socket
+ if (socket_ && !(state_ == STATE_CLOSED || state_ == STATE_CLOSING)) {
+ code = socket_->Close();
+ }
+
+ delete[] write_buffer_;
+ write_buffer_ = NULL;
+ write_buffer_length_ = 0;
+ write_buffer_capacity_ = 0;
+
+ if (code) {
+ SetWSAError(code);
+ return false;
+ }
+ return true;
+}
+
+bool XmppSocketAdapter::Connect(const talk_base::SocketAddress& addr) {
+ if (state_ != STATE_CLOSED) {
+ SetError(ERROR_WRONGSTATE);
+ return false;
+ }
+
+ LOG(LS_INFO) << "XmppSocketAdapter::Connect(" << addr.ToString() << ")";
+
+ // Clean up any previous socket - cannot delete socket on close because
+ // close happens during the child socket's stack callback.
+ if (socket_) {
+ delete socket_;
+ socket_ = NULL;
+ }
+
+ talk_base::AsyncSocket* socket =
+ talk_base::Thread::Current()->socketserver()
+ ->CreateAsyncSocket(SOCK_STREAM);
+ if (!socket) {
+ SetWSAError(WSA_NOT_ENOUGH_MEMORY);
+ return false;
+ }
+
+ if (firewall_) {
+ // TODO(sync): Change this to make WSAAsyncSockets support current thread
+ // socket server
+ talk_base::FirewallSocketServer* fw =
+ static_cast<talk_base::FirewallSocketServer*>(
+ talk_base::Thread::Current()->socketserver());
+ socket = fw->WrapSocket(socket, SOCK_STREAM);
+ }
+
+ if (proxy_.type) {
+ talk_base::AsyncSocket* proxy_socket = 0;
+ if (proxy_.type == talk_base::PROXY_SOCKS5) {
+ proxy_socket = new talk_base::AsyncSocksProxySocket(
+ socket, proxy_.address, proxy_.username, proxy_.password);
+ } else {
+ // Note: we are trying unknown proxies as HTTPS currently
+ proxy_socket = new talk_base::AsyncHttpsProxySocket(socket,
+ GetUserAgentString(), proxy_.address,
+ proxy_.username, proxy_.password);
+ }
+ if (!proxy_socket) {
+ SetWSAError(WSA_NOT_ENOUGH_MEMORY);
+ delete socket;
+ return false;
+ }
+ socket = proxy_socket; // for our purposes the proxy is now the socket
+ }
+
+// #if defined(PRODUCTION)
+ if (protocol_ == cricket::PROTO_SSLTCP) {
+ talk_base::AsyncSocket *fake_ssl_socket =
+ new talk_base::AsyncSSLSocket(socket);
+ if (!fake_ssl_socket) {
+ SetWSAError(WSA_NOT_ENOUGH_MEMORY);
+ delete socket;
+ return false;
+ }
+ socket = fake_ssl_socket; // for our purposes the SSL socket is the socket
+ }
+// #endif // PRODUCTION
+
+#if defined(FEATURE_ENABLE_SSL)
+ talk_base::SSLAdapter* ssl = talk_base::SSLAdapter::Create(socket);
+ socket = ssl;
+#endif
+
+// #if !defined(PRODUCTION)
+// if (protocol_ == cricket::PROTO_SSLTCP) {
+// ssl->set_ignore_bad_cert(true);
+// ssl->StartSSL(addr.hostname().c_str(), true);
+// }
+// #endif // PRODUCTION
+
+ socket->SignalReadEvent.connect(this, &XmppSocketAdapter::OnReadEvent);
+ socket->SignalWriteEvent.connect(this, &XmppSocketAdapter::OnWriteEvent);
+ socket->SignalConnectEvent.connect(this, &XmppSocketAdapter::OnConnectEvent);
+ socket->SignalCloseEvent.connect(this, &XmppSocketAdapter::OnCloseEvent);
+
+ // The linux implementation of socket::Connect
+ // returns an error when the connect didn't complete
+ // yet. This can be distinguished from a failure
+ // because socket::IsBlocking is true. Perhaps,
+ // the linux implementation should be made to
+ // behave like the windows version which doesn't do this,
+ // but it seems to be a pattern with these methods
+ // that they return an error if the operation didn't
+ // complete in a sync fashion and one has to check IsBlocking
+ // to tell if was a "real" error.
+ if (socket->Connect(addr) == SOCKET_ERROR && !socket->IsBlocking()) {
+ SetWSAError(socket->GetError());
+ delete socket;
+ return false;
+ }
+
+ socket_ = socket;
+ state_ = STATE_CONNECTING;
+ return true;
+}
+
+bool XmppSocketAdapter::Read(char* data, size_t len, size_t* len_read) {
+ if (len_read)
+ *len_read = 0;
+
+ if (state_ <= STATE_CLOSING) {
+ SetError(ERROR_WRONGSTATE);
+ return false;
+ }
+
+ ASSERT(socket_ != NULL);
+
+ if (IsOpen()) {
+ int result = socket_->Recv(data, len);
+ if (result < 0) {
+ if (!socket_->IsBlocking()) {
+ SetWSAError(socket_->GetError());
+ return false;
+ }
+
+ result = 0;
+ }
+
+ if (len_read)
+ *len_read = result;
+ }
+
+ return true;
+}
+
+bool XmppSocketAdapter::Write(const char* data, size_t len) {
+ if (state_ <= STATE_CLOSING) {
+ // There may be data in a buffer that gets lost. Too bad!
+ SetError(ERROR_WRONGSTATE);
+ return false;
+ }
+
+ ASSERT(socket_ != NULL);
+
+ size_t sent = 0;
+
+ // try an immediate write when there is no buffer
+ // and we aren't in SSL mode or opening the connection
+ if (write_buffer_length_ == 0 && IsOpen()) {
+ int result = socket_->Send(data, len);
+ if (result < 0) {
+ if (!socket_->IsBlocking()) {
+ SetWSAError(socket_->GetError());
+ return false;
+ }
+ result = 0;
+ }
+
+ sent = static_cast<size_t>(result);
+ }
+
+ // Buffer what we didn't send
+ if (sent < len) {
+ QueueWriteData(data + sent, len - sent);
+ }
+
+ // Service the socket right away to push the written data out in SSL mode
+ return HandleWritable();
+}
+
+bool XmppSocketAdapter::Close() {
+ if (state_ == STATE_CLOSING) {
+ return false; // avoid recursion, but not unexpected
+ }
+ if (state_ == STATE_CLOSED) {
+ // in theory should not be trying to re-InternalClose.
+ SetError(ERROR_WRONGSTATE);
+ return false;
+ }
+
+ // todo: deal with flushing close
+ // (flush, don't do reads, clean ssl)
+
+ // If we've gotten to the point where we really do have a socket underneath
+ // then close it. It should call us back to tell us it is closed, and
+ // NotifyClose will be called. We indicate "closing" state so that we
+ // do not recusively try to keep closing the socket.
+ if (socket_) {
+ state_ = STATE_CLOSING;
+ socket_->Close();
+ }
+
+ // If we didn't get the callback, then we better make sure we signal
+ // closed.
+ if (state_ != STATE_CLOSED) {
+ // The socket was closed manually, not directly due to error.
+ if (error_ != ERROR_NONE) {
+ LOG(LS_INFO) << "XmppSocketAdapter::Close - previous Error: " << error_
+ << " WSAError: " << wsa_error_;
+ error_ = ERROR_NONE;
+ wsa_error_ = 0;
+ }
+ NotifyClose();
+ }
+ return true;
+}
+
+void XmppSocketAdapter::NotifyClose() {
+ if (state_ == STATE_CLOSED) {
+ SetError(ERROR_WRONGSTATE);
+ } else {
+ LOG(LS_INFO) << "XmppSocketAdapter::NotifyClose - Error: " << error_
+ << " WSAError: " << wsa_error_;
+ state_ = STATE_CLOSED;
+ SignalClosed();
+ FreeState();
+ }
+}
+
+void XmppSocketAdapter::OnConnectEvent(talk_base::AsyncSocket *socket) {
+ if (state_ == STATE_CONNECTING) {
+ state_ = STATE_OPEN;
+ LOG(LS_INFO) << "XmppSocketAdapter::OnConnectEvent - STATE_OPEN";
+ SignalConnected();
+#if defined(FEATURE_ENABLE_SSL)
+ } else if (state_ == STATE_TLS_CONNECTING) {
+ state_ = STATE_TLS_OPEN;
+ LOG(LS_INFO) << "XmppSocketAdapter::OnConnectEvent - STATE_TLS_OPEN";
+ SignalSSLConnected();
+ if (write_buffer_length_ > 0) {
+ HandleWritable();
+ }
+#endif // defined(FEATURE_ENABLE_SSL)
+ } else {
+ LOG(LS_INFO) << "XmppSocketAdapter::OnConnectEvent - state is " << state_;
+ ASSERT(false);
+ }
+}
+
+void XmppSocketAdapter::OnReadEvent(talk_base::AsyncSocket *socket) {
+ HandleReadable();
+}
+
+void XmppSocketAdapter::OnWriteEvent(talk_base::AsyncSocket *socket) {
+ HandleWritable();
+}
+
+void XmppSocketAdapter::OnCloseEvent(talk_base::AsyncSocket *socket,
+ int error) {
+ LOG(LS_INFO) << "XmppSocketAdapter::OnCloseEvent(" << error << ")";
+ SetWSAError(error);
+ if (error == SOCKET_EACCES) {
+ SignalAuthenticationError(); // proxy needs authentication
+ }
+ NotifyClose();
+}
+
+#if defined(FEATURE_ENABLE_SSL)
+bool XmppSocketAdapter::StartTls(const std::string& verify_host_name) {
+ if (state_ != STATE_OPEN) {
+ SetError(ERROR_WRONGSTATE);
+ return false;
+ }
+
+ state_ = STATE_TLS_CONNECTING;
+
+ ASSERT(write_buffer_length_ == 0);
+
+ talk_base::SSLAdapter* ssl_adapter =
+ static_cast<talk_base::SSLAdapter*>(socket_);
+
+ if (allow_unverified_certs_) {
+ ssl_adapter->set_ignore_bad_cert(true);
+ }
+
+ if (ssl_adapter->StartSSL(verify_host_name.c_str(), false) != 0) {
+ state_ = STATE_OPEN;
+ SetError(ERROR_SSL);
+ return false;
+ }
+
+ return true;
+}
+#endif // defined(FEATURE_ENABLE_SSL)
+
+void XmppSocketAdapter::QueueWriteData(const char* data, size_t len) {
+ // expand buffer if needed
+ if (write_buffer_length_ + len > write_buffer_capacity_) {
+ size_t new_capacity = 1024;
+ while (new_capacity < write_buffer_length_ + len) {
+ new_capacity = new_capacity * 2;
+ }
+ char* new_buffer = new char[new_capacity];
+ ASSERT(write_buffer_length_ <= 64000);
+ memcpy(new_buffer, write_buffer_, write_buffer_length_);
+ delete[] write_buffer_;
+ write_buffer_ = new_buffer;
+ write_buffer_capacity_ = new_capacity;
+ }
+
+ // copy data into the end of buffer
+ memcpy(write_buffer_ + write_buffer_length_, data, len);
+ write_buffer_length_ += len;
+}
+
+void XmppSocketAdapter::FlushWriteQueue(Error* error, int* wsa_error) {
+ ASSERT(error && wsa_error);
+
+ size_t flushed = 0;
+ while (flushed < write_buffer_length_) {
+ int sent = socket_->Send(write_buffer_ + flushed,
+ static_cast<int>(write_buffer_length_ - flushed));
+ if (sent < 0) {
+ if (!socket_->IsBlocking()) {
+ *error = ERROR_WINSOCK;
+ *wsa_error = socket_->GetError();
+ }
+ break;
+ }
+ flushed += static_cast<size_t>(sent);
+ }
+
+ // remove flushed memory
+ write_buffer_length_ -= flushed;
+ memmove(write_buffer_, write_buffer_ + flushed, write_buffer_length_);
+
+ // when everything is flushed, deallocate the buffer if it's gotten big
+ if (write_buffer_length_ == 0) {
+ if (write_buffer_capacity_ > 8192) {
+ delete[] write_buffer_;
+ write_buffer_ = NULL;
+ write_buffer_capacity_ = 0;
+ }
+ }
+}
+
+void XmppSocketAdapter::SetError(Error error) {
+ if (error_ == ERROR_NONE) {
+ error_ = error;
+ }
+}
+
+void XmppSocketAdapter::SetWSAError(int error) {
+ if (error_ == ERROR_NONE && error != 0) {
+ error_ = ERROR_WINSOCK;
+ wsa_error_ = error;
+ }
+}
+
+bool XmppSocketAdapter::HandleReadable() {
+ if (!IsOpen())
+ return false;
+
+ SignalRead();
+ return true;
+}
+
+bool XmppSocketAdapter::HandleWritable() {
+ if (!IsOpen())
+ return false;
+
+ Error error = ERROR_NONE;
+ int wsa_error = 0;
+ FlushWriteQueue(&error, &wsa_error);
+ if (error != ERROR_NONE) {
+ Close();
+ return false;
+ }
+ return true;
+}
+} // namespace notifier
diff --git a/chrome/browser/sync/notifier/communicator/xmpp_socket_adapter.h b/chrome/browser/sync/notifier/communicator/xmpp_socket_adapter.h
new file mode 100644
index 0000000..7e42988
--- /dev/null
+++ b/chrome/browser/sync/notifier/communicator/xmpp_socket_adapter.h
@@ -0,0 +1,85 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_SYNC_NOTIFIER_COMMUNICATOR_XMPP_SOCKET_ADAPTER_H_
+#define CHROME_BROWSER_SYNC_NOTIFIER_COMMUNICATOR_XMPP_SOCKET_ADAPTER_H_
+#include <string>
+
+#include "talk/base/asyncsocket.h"
+#include "talk/xmpp/asyncsocket.h"
+#include "talk/xmpp/xmppclientsettings.h"
+#include "talk/xmpp/xmppengine.h"
+
+#ifndef _WIN32
+// Additional errors used by us from Win32 headers
+#define SEC_E_CERT_EXPIRED static_cast<int>(0x80090328L)
+#define WSA_NOT_ENOUGH_MEMORY ENOMEM
+#endif
+
+namespace notifier {
+
+class XmppSocketAdapter : public buzz::AsyncSocket,
+ public sigslot::has_slots<> {
+ public:
+ XmppSocketAdapter(const buzz::XmppClientSettings& xcs,
+ bool allow_unverified_certs);
+ virtual ~XmppSocketAdapter();
+
+ virtual State state() { return state_; }
+ virtual Error error() { return error_; }
+ virtual int GetError() { return wsa_error_; }
+
+ void set_firewall(bool firewall) { firewall_ = firewall; }
+
+ virtual bool Connect(const talk_base::SocketAddress& addr);
+ virtual bool Read(char* data, size_t len, size_t* len_read);
+ virtual bool Write(const char* data, size_t len);
+ virtual bool Close();
+
+#if defined(FEATURE_ENABLE_SSL)
+ bool StartTls(const std::string& domainname);
+ bool IsOpen() const { return state_ == STATE_OPEN
+ || state_ == STATE_TLS_OPEN; }
+#else
+ bool IsOpen() const { return state_ == STATE_OPEN; }
+#endif
+
+ sigslot::signal0<> SignalAuthenticationError;
+
+ private:
+ // return false if the socket is closed
+ bool HandleReadable();
+ bool HandleWritable();
+
+ State state_;
+ Error error_;
+ int wsa_error_;
+
+ talk_base::AsyncSocket* socket_;
+ cricket::ProtocolType protocol_;
+ talk_base::ProxyInfo proxy_;
+ bool firewall_;
+ char* write_buffer_;
+ size_t write_buffer_length_;
+ size_t write_buffer_capacity_;
+ bool allow_unverified_certs_;
+
+ bool FreeState();
+ void NotifyClose();
+
+ void OnReadEvent(talk_base::AsyncSocket* socket);
+ void OnWriteEvent(talk_base::AsyncSocket* socket);
+ void OnConnectEvent(talk_base::AsyncSocket* socket);
+ void OnCloseEvent(talk_base::AsyncSocket* socket, int error);
+
+ void QueueWriteData(const char* data, size_t len);
+ void FlushWriteQueue(Error* error, int* wsa_error);
+
+ void SetError(Error error);
+ void SetWSAError(int error);
+ DISALLOW_COPY_AND_ASSIGN(XmppSocketAdapter);
+};
+} // namespace notifier
+
+#endif // CHROME_BROWSER_SYNC_NOTIFIER_COMMUNICATOR_XMPP_SOCKET_ADAPTER_H_