summaryrefslogtreecommitdiffstats
path: root/jingle
diff options
context:
space:
mode:
Diffstat (limited to 'jingle')
-rw-r--r--jingle/jingle.gyp11
-rw-r--r--jingle/notifier/base/notifier_options.h13
-rw-r--r--jingle/notifier/base/signal_thread_task.h96
-rw-r--r--jingle/notifier/base/ssl_adapter.cc27
-rw-r--r--jingle/notifier/base/ssl_adapter.h33
-rw-r--r--jingle/notifier/base/static_assert.h21
-rw-r--r--jingle/notifier/communicator/login.cc7
-rw-r--r--jingle/notifier/communicator/login.h2
-rw-r--r--jingle/notifier/communicator/product_info.cc15
-rw-r--r--jingle/notifier/communicator/product_info.h15
-rw-r--r--jingle/notifier/communicator/single_login_attempt.cc230
-rw-r--r--jingle/notifier/communicator/single_login_attempt.h11
-rw-r--r--jingle/notifier/communicator/ssl_socket_adapter.cc386
-rw-r--r--jingle/notifier/communicator/ssl_socket_adapter.h145
-rw-r--r--jingle/notifier/communicator/xmpp_connection_generator.cc2
-rw-r--r--jingle/notifier/communicator/xmpp_socket_adapter.cc429
-rw-r--r--jingle/notifier/communicator/xmpp_socket_adapter.h87
-rw-r--r--jingle/notifier/listener/mediator_thread_impl.cc46
-rw-r--r--jingle/notifier/listener/mediator_thread_impl.h8
19 files changed, 1558 insertions, 26 deletions
diff --git a/jingle/jingle.gyp b/jingle/jingle.gyp
index 79ee350..2f01fff 100644
--- a/jingle/jingle.gyp
+++ b/jingle/jingle.gyp
@@ -17,11 +17,15 @@
'sources': [
'notifier/base/chrome_async_socket.cc',
'notifier/base/chrome_async_socket.h',
+ 'notifier/base/signal_thread_task.h',
+ 'notifier/base/ssl_adapter.h',
+ 'notifier/base/ssl_adapter.cc',
'notifier/base/fake_ssl_client_socket.cc',
'notifier/base/fake_ssl_client_socket.h',
'notifier/base/notification_method.h',
'notifier/base/notification_method.cc',
'notifier/base/notifier_options.h',
+ 'notifier/base/static_assert.h',
'notifier/base/task_pump.cc',
'notifier/base/task_pump.h',
'notifier/base/xmpp_client_socket_factory.cc',
@@ -40,10 +44,16 @@
'notifier/communicator/login_failure.h',
'notifier/communicator/login_settings.cc',
'notifier/communicator/login_settings.h',
+ 'notifier/communicator/product_info.cc',
+ 'notifier/communicator/product_info.h',
'notifier/communicator/single_login_attempt.cc',
'notifier/communicator/single_login_attempt.h',
+ 'notifier/communicator/ssl_socket_adapter.cc',
+ 'notifier/communicator/ssl_socket_adapter.h',
'notifier/communicator/xmpp_connection_generator.cc',
'notifier/communicator/xmpp_connection_generator.h',
+ 'notifier/communicator/xmpp_socket_adapter.cc',
+ 'notifier/communicator/xmpp_socket_adapter.h',
'notifier/listener/listen_task.cc',
'notifier/listener/listen_task.h',
'notifier/listener/mediator_thread.h',
@@ -67,6 +77,7 @@
'defines' : [
'_CRT_SECURE_NO_WARNINGS',
'_USE_32BIT_TIME_T',
+ 'kXmppProductName="chromium-sync"',
],
'dependencies': [
'../base/base.gyp:base',
diff --git a/jingle/notifier/base/notifier_options.h b/jingle/notifier/base/notifier_options.h
index 289b7d8..32374cf 100644
--- a/jingle/notifier/base/notifier_options.h
+++ b/jingle/notifier/base/notifier_options.h
@@ -12,16 +12,23 @@ namespace notifier {
struct NotifierOptions {
NotifierOptions()
- : try_ssltcp_first(false),
+ : use_chrome_async_socket(true),
+ try_ssltcp_first(false),
notification_method(kDefaultNotificationMethod) {}
- NotifierOptions(const bool try_ssltcp_first,
+ NotifierOptions(const bool use_chrome_async_socket,
+ const bool try_ssltcp_first,
const net::HostPortPair& xmpp_host_port,
NotificationMethod notification_method)
- : try_ssltcp_first(try_ssltcp_first),
+ : use_chrome_async_socket(use_chrome_async_socket),
+ try_ssltcp_first(try_ssltcp_first),
xmpp_host_port(xmpp_host_port),
notification_method(notification_method) {}
+ // Indicates whether to use the chrome-socket-based buzz::AsyncSocket
+ // implementation for notifications.
+ bool use_chrome_async_socket;
+
// Indicates that the SSLTCP port (443) is to be tried before the the XMPP
// port (5222) during login.
bool try_ssltcp_first;
diff --git a/jingle/notifier/base/signal_thread_task.h b/jingle/notifier/base/signal_thread_task.h
new file mode 100644
index 0000000..8a6ff27
--- /dev/null
+++ b/jingle/notifier/base/signal_thread_task.h
@@ -0,0 +1,96 @@
+// 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 JINGLE_NOTIFIER_BASE_SIGNAL_THREAD_TASK_H_
+#define JINGLE_NOTIFIER_BASE_SIGNAL_THREAD_TASK_H_
+
+#include "base/logging.h"
+#include "talk/base/common.h"
+#include "talk/base/signalthread.h"
+#include "talk/base/sigslot.h"
+#include "talk/base/task.h"
+
+namespace notifier {
+
+template<class T>
+class SignalThreadTask : public talk_base::Task,
+ public sigslot::has_slots<> {
+ public:
+ // Takes ownership of signal_thread.
+ SignalThreadTask(talk_base::Task* task_parent, T** signal_thread)
+ : talk_base::Task(task_parent),
+ signal_thread_(NULL),
+ finished_(false) {
+ SetSignalThread(signal_thread);
+ }
+
+ virtual ~SignalThreadTask() {
+ ClearSignalThread();
+ }
+
+ virtual void Stop() {
+ Task::Stop();
+ ClearSignalThread();
+ }
+
+ virtual int ProcessStart() {
+ DCHECK_EQ(GetState(), talk_base::Task::STATE_START);
+ signal_thread_->SignalWorkDone.connect(
+ this,
+ &SignalThreadTask<T>::OnWorkDone);
+ signal_thread_->Start();
+ return talk_base::Task::STATE_RESPONSE;
+ }
+
+ int ProcessResponse() {
+ if (!finished_) {
+ return talk_base::Task::STATE_BLOCKED;
+ }
+ SignalWorkDone(signal_thread_);
+ ClearSignalThread();
+ return talk_base::Task::STATE_DONE;
+ }
+
+ sigslot::signal1<T*> SignalWorkDone;
+
+ private:
+ // Takes ownership of signal_thread.
+ void SetSignalThread(T** signal_thread) {
+ DCHECK(!signal_thread_);
+ DCHECK(signal_thread);
+ DCHECK(*signal_thread);
+ // No one should be listening to the signal thread for work done.
+ // They should be using this class instead. Unfortunately, we
+ // can't verify this.
+
+ signal_thread_ = *signal_thread;
+
+ // Helps callers not to use signal thread after this point since this class
+ // has taken ownership (and avoid the error of doing
+ // signal_thread->Start()).
+ *signal_thread = NULL;
+ }
+
+ void OnWorkDone(talk_base::SignalThread* signal_thread) {
+ DCHECK_EQ(signal_thread, signal_thread_);
+ finished_ = true;
+ Wake();
+ }
+
+ void ClearSignalThread() {
+ if (signal_thread_) {
+ // Don't wait on the thread destruction, or we may deadlock.
+ signal_thread_->Destroy(false);
+ signal_thread_ = NULL;
+ }
+ }
+
+ T* signal_thread_;
+ bool finished_;
+ DISALLOW_COPY_AND_ASSIGN(SignalThreadTask);
+};
+
+} // namespace notifier
+
+#endif // JINGLE_NOTIFIER_BASE_SIGNAL_THREAD_TASK_H_
diff --git a/jingle/notifier/base/ssl_adapter.cc b/jingle/notifier/base/ssl_adapter.cc
new file mode 100644
index 0000000..2111244
--- /dev/null
+++ b/jingle/notifier/base/ssl_adapter.cc
@@ -0,0 +1,27 @@
+// 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 "jingle/notifier/base/ssl_adapter.h"
+
+#if defined(OS_WIN)
+#include "talk/base/ssladapter.h"
+#else
+#include "jingle/notifier/communicator/ssl_socket_adapter.h"
+#endif
+
+namespace notifier {
+
+talk_base::SSLAdapter* CreateSSLAdapter(talk_base::AsyncSocket* socket) {
+ talk_base::SSLAdapter* ssl_adapter =
+#if defined(OS_WIN)
+ talk_base::SSLAdapter::Create(socket);
+#else
+ notifier::SSLSocketAdapter::Create(socket);
+#endif
+ DCHECK(ssl_adapter);
+ return ssl_adapter;
+}
+
+} // namespace notifier
+
diff --git a/jingle/notifier/base/ssl_adapter.h b/jingle/notifier/base/ssl_adapter.h
new file mode 100644
index 0000000..32517cd
--- /dev/null
+++ b/jingle/notifier/base/ssl_adapter.h
@@ -0,0 +1,33 @@
+// 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 JINGLE_NOTIFIER_BASE_SSL_ADAPTER_H_
+#define JINGLE_NOTIFIER_BASE_SSL_ADAPTER_H_
+
+namespace talk_base {
+class AsyncSocket;
+class SSLAdapter;
+} // namespace talk_base
+
+namespace notifier {
+
+// Wraps the given socket in a platform-dependent SSLAdapter
+// implementation.
+talk_base::SSLAdapter* CreateSSLAdapter(talk_base::AsyncSocket* socket);
+
+// Utility template class that overrides CreateSSLAdapter() to use the
+// above function.
+template <class SocketFactory>
+class SSLAdapterSocketFactory : public SocketFactory {
+ public:
+ virtual talk_base::SSLAdapter* CreateSSLAdapter(
+ talk_base::AsyncSocket* socket) {
+ return ::notifier::CreateSSLAdapter(socket);
+ }
+};
+
+} // namespace notifier
+
+#endif // JINGLE_NOTIFIER_BASE_SSL_ADAPTER_H_
+
diff --git a/jingle/notifier/base/static_assert.h b/jingle/notifier/base/static_assert.h
new file mode 100644
index 0000000..58a8fe4
--- /dev/null
+++ b/jingle/notifier/base/static_assert.h
@@ -0,0 +1,21 @@
+// 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 JINGLE_NOTIFIER_BASE_STATIC_ASSERT_H_
+#define JINGLE_NOTIFIER_BASE_STATIC_ASSERT_H_
+
+template<bool> struct STATIC_ASSERTION_FAILURE;
+
+template<> struct STATIC_ASSERTION_FAILURE<true> {
+ enum { value = 1 };
+};
+
+template<int> struct static_assert_test{};
+
+#define STATIC_ASSERT(B) \
+typedef static_assert_test<\
+ sizeof(STATIC_ASSERTION_FAILURE< (bool)( B ) >)>\
+ static_assert_typedef_ ## __LINE__
+
+#endif // JINGLE_NOTIFIER_BASE_STATIC_ASSERT_H_
diff --git a/jingle/notifier/communicator/login.cc b/jingle/notifier/communicator/login.cc
index cd0cbbf..041bc67 100644
--- a/jingle/notifier/communicator/login.cc
+++ b/jingle/notifier/communicator/login.cc
@@ -11,6 +11,7 @@
#include "base/time.h"
#include "jingle/notifier/communicator/connection_options.h"
#include "jingle/notifier/communicator/login_settings.h"
+#include "jingle/notifier/communicator/product_info.h"
#include "jingle/notifier/communicator/single_login_attempt.h"
#include "net/base/host_port_pair.h"
#include "talk/base/common.h"
@@ -31,6 +32,7 @@ namespace notifier {
static const int kRedirectTimeoutMinutes = 5;
Login::Login(talk_base::TaskParent* parent,
+ bool use_chrome_async_socket,
const buzz::XmppClientSettings& user_settings,
const ConnectionOptions& options,
std::string lang,
@@ -41,6 +43,7 @@ Login::Login(talk_base::TaskParent* parent,
bool try_ssltcp_first,
bool proxy_only)
: parent_(parent),
+ use_chrome_async_socket_(use_chrome_async_socket),
login_settings_(new LoginSettings(user_settings,
options,
lang,
@@ -80,7 +83,9 @@ void Login::StartConnection() {
LOG(INFO) << "Starting connection...";
single_attempt_ = new SingleLoginAttempt(parent_,
- login_settings_.get());
+ login_settings_.get(),
+ use_chrome_async_socket_,
+ true);
// Do the signaling hook-ups.
single_attempt_->SignalUnexpectedDisconnect.connect(
diff --git a/jingle/notifier/communicator/login.h b/jingle/notifier/communicator/login.h
index 27b2543..84e89a8 100644
--- a/jingle/notifier/communicator/login.h
+++ b/jingle/notifier/communicator/login.h
@@ -49,6 +49,7 @@ class Login : public net::NetworkChangeNotifier::Observer,
public:
// firewall may be NULL.
Login(talk_base::TaskParent* parent,
+ bool use_chrome_async_socket,
const buzz::XmppClientSettings& user_settings,
const ConnectionOptions& options,
std::string lang,
@@ -97,6 +98,7 @@ class Login : public net::NetworkChangeNotifier::Observer,
void DoReconnect();
talk_base::TaskParent* parent_;
+ bool use_chrome_async_socket_;
scoped_ptr<LoginSettings> login_settings_;
LoginConnectionState state_;
SingleLoginAttempt* single_attempt_;
diff --git a/jingle/notifier/communicator/product_info.cc b/jingle/notifier/communicator/product_info.cc
new file mode 100644
index 0000000..c1deafb
--- /dev/null
+++ b/jingle/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/jingle/notifier/communicator/product_info.h b/jingle/notifier/communicator/product_info.h
new file mode 100644
index 0000000..9e8e5d0
--- /dev/null
+++ b/jingle/notifier/communicator/product_info.h
@@ -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.
+
+#ifndef JINGLE_NOTIFIER_COMMUNICATOR_PRODUCT_INFO_H_
+#define JINGLE_NOTIFIER_COMMUNICATOR_PRODUCT_INFO_H_
+
+#include <string>
+
+namespace notifier {
+std::string GetUserAgentString();
+std::string GetProductSignature();
+} // namespace notifier
+
+#endif // JINGLE_NOTIFIER_COMMUNICATOR_PRODUCT_INFO_H_
diff --git a/jingle/notifier/communicator/single_login_attempt.cc b/jingle/notifier/communicator/single_login_attempt.cc
index 4db7758..ccdf0ce 100644
--- a/jingle/notifier/communicator/single_login_attempt.cc
+++ b/jingle/notifier/communicator/single_login_attempt.cc
@@ -18,9 +18,16 @@
#include "jingle/notifier/communicator/gaia_token_pre_xmpp_auth.h"
#include "jingle/notifier/communicator/login_failure.h"
#include "jingle/notifier/communicator/login_settings.h"
+#include "jingle/notifier/communicator/product_info.h"
#include "jingle/notifier/communicator/xmpp_connection_generator.h"
+#include "jingle/notifier/communicator/xmpp_socket_adapter.h"
#include "net/base/ssl_config_service.h"
#include "net/socket/client_socket_factory.h"
+#include "talk/base/asynchttprequest.h"
+#include "talk/base/firewallsocketserver.h"
+#include "talk/base/signalthread.h"
+#include "talk/base/taskrunner.h"
+#include "talk/base/win32socketinit.h"
#include "talk/xmllite/xmlelement.h"
#include "talk/xmpp/xmppclient.h"
#include "talk/xmpp/xmppclientsettings.h"
@@ -54,15 +61,23 @@ static void GetClientErrorInformation(
}
SingleLoginAttempt::SingleLoginAttempt(talk_base::TaskParent* parent,
- LoginSettings* login_settings)
+ LoginSettings* login_settings,
+ bool use_chrome_async_socket,
+ bool successful_connection)
: talk_base::Task(parent),
+ use_chrome_async_socket_(use_chrome_async_socket),
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) {
+#if defined(OS_WIN)
+ talk_base::EnsureWinsockInit();
+#endif
connection_generator_.reset(new XmppConnectionGenerator(
this,
login_settings_->host_resolver(),
@@ -139,7 +154,14 @@ void SingleLoginAttempt::OnAttemptedAllConnections(
LOG(INFO) << "Connection failed with error " << code_;
- SignalNeedAutoReconnect();
+ // We were connected and we had a problem.
+ if (successful_connection_) {
+ SignalNeedAutoReconnect();
+ // Expect to be deleted at this point.
+ return;
+ }
+
+ DiagnoseConnectionError();
}
void SingleLoginAttempt::UseNextConnection() {
@@ -203,24 +225,39 @@ void SingleLoginAttempt::OnCertificateExpired() {
buzz::AsyncSocket* SingleLoginAttempt::CreateSocket(
const buzz::XmppClientSettings& xcs) {
- bool use_fake_ssl_client_socket =
- (xcs.protocol() == cricket::PROTO_SSLTCP);
- net::ClientSocketFactory* const client_socket_factory =
- new XmppClientSocketFactory(
- net::ClientSocketFactory::GetDefaultFactory(),
- use_fake_ssl_client_socket);
- // The default SSLConfig is good enough for us for now.
- const net::SSLConfig ssl_config;
- // A read buffer of 64k ought to be sufficient.
- const size_t kReadBufSize = 64U * 1024U;
- // This number was taken from a similar number in
- // XmppSocketAdapter.
- const size_t kWriteBufSize = 64U * 1024U;
- // TODO(akalin): Use a real NetLog.
- net::NetLog* const net_log = NULL;
- return new ChromeAsyncSocket(
- client_socket_factory, ssl_config,
- kReadBufSize, kWriteBufSize, net_log);
+ if (use_chrome_async_socket_) {
+ bool use_fake_ssl_client_socket =
+ (xcs.protocol() == cricket::PROTO_SSLTCP);
+ net::ClientSocketFactory* const client_socket_factory =
+ new XmppClientSocketFactory(
+ net::ClientSocketFactory::GetDefaultFactory(),
+ use_fake_ssl_client_socket);
+ // The default SSLConfig is good enough for us for now.
+ const net::SSLConfig ssl_config;
+ // A read buffer of 64k ought to be sufficient.
+ const size_t kReadBufSize = 64U * 1024U;
+ // This number was taken from a similar number in
+ // XmppSocketAdapter.
+ const size_t kWriteBufSize = 64U * 1024U;
+ // TODO(akalin): Use a real NetLog.
+ net::NetLog* const net_log = NULL;
+ return new ChromeAsyncSocket(
+ client_socket_factory, ssl_config,
+ kReadBufSize, kWriteBufSize, net_log);
+ }
+ // TODO(akalin): Always use ChromeAsyncSocket and get rid of this
+ // code.
+ 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(
@@ -230,6 +267,147 @@ buzz::PreXmppAuth* SingleLoginAttempt::CreatePreXmppAuth(
jid.Str(), xcs.auth_cookie(), xcs.token_service());
}
+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;
+ DCHECK(connection_generator_.get());
+ 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) {
+ DCHECK(thread);
+
+ 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;
@@ -241,9 +419,11 @@ void SingleLoginAttempt::OnClientStateChange(buzz::XmppEngine::State state) {
case buzz::XmppEngine::STATE_NONE:
case buzz::XmppEngine::STATE_START:
case buzz::XmppEngine::STATE_OPENING:
- case buzz::XmppEngine::STATE_OPEN:
// Do nothing.
break;
+ case buzz::XmppEngine::STATE_OPEN:
+ successful_connection_ = true;
+ break;
case buzz::XmppEngine::STATE_CLOSED:
OnClientStateChangeClosed(previous_state);
break;
@@ -319,6 +499,14 @@ void SingleLoginAttempt::HandleConnectionError(
// 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".
diff --git a/jingle/notifier/communicator/single_login_attempt.h b/jingle/notifier/communicator/single_login_attempt.h
index 1b9900c..8b4f93b 100644
--- a/jingle/notifier/communicator/single_login_attempt.h
+++ b/jingle/notifier/communicator/single_login_attempt.h
@@ -47,7 +47,9 @@ class XmppConnectionGenerator;
class SingleLoginAttempt : public talk_base::Task, public sigslot::has_slots<> {
public:
SingleLoginAttempt(talk_base::TaskParent* parent,
- LoginSettings* login_settings);
+ LoginSettings* login_settings,
+ bool use_chrome_async_socket,
+ bool successful_connection);
~SingleLoginAttempt();
virtual int ProcessStart();
void UseNextConnection();
@@ -105,18 +107,25 @@ class SingleLoginAttempt : public talk_base::Task, public sigslot::has_slots<> {
const buzz::XmlElement* stream_error);
void HandleConnectionPasswordError();
+ 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 use_chrome_async_socket_;
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_;
diff --git a/jingle/notifier/communicator/ssl_socket_adapter.cc b/jingle/notifier/communicator/ssl_socket_adapter.cc
new file mode 100644
index 0000000..cac3041
--- /dev/null
+++ b/jingle/notifier/communicator/ssl_socket_adapter.cc
@@ -0,0 +1,386 @@
+// Copyright (c) 2010 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 "jingle/notifier/communicator/ssl_socket_adapter.h"
+
+#include "base/compiler_specific.h"
+#include "base/message_loop.h"
+#include "net/base/address_list.h"
+#include "net/base/net_errors.h"
+#include "net/base/ssl_config_service.h"
+#include "net/base/sys_addrinfo.h"
+#include "net/socket/client_socket_factory.h"
+#include "net/url_request/url_request_context.h"
+
+namespace notifier {
+
+namespace {
+
+// Convert values from <errno.h> to values from "net/base/net_errors.h"
+int MapPosixError(int err) {
+ // There are numerous posix error codes, but these are the ones we thus far
+ // find interesting.
+ switch (err) {
+ case EAGAIN:
+#if EWOULDBLOCK != EAGAIN
+ case EWOULDBLOCK:
+#endif
+ return net::ERR_IO_PENDING;
+ case ENETDOWN:
+ return net::ERR_INTERNET_DISCONNECTED;
+ case ETIMEDOUT:
+ return net::ERR_TIMED_OUT;
+ case ECONNRESET:
+ case ENETRESET: // Related to keep-alive
+ return net::ERR_CONNECTION_RESET;
+ case ECONNABORTED:
+ return net::ERR_CONNECTION_ABORTED;
+ case ECONNREFUSED:
+ return net::ERR_CONNECTION_REFUSED;
+ case EHOSTUNREACH:
+ case ENETUNREACH:
+ return net::ERR_ADDRESS_UNREACHABLE;
+ case EADDRNOTAVAIL:
+ return net::ERR_ADDRESS_INVALID;
+ case 0:
+ return net::OK;
+ default:
+ LOG(WARNING) << "Unknown error " << err << " mapped to net::ERR_FAILED";
+ return net::ERR_FAILED;
+ }
+}
+
+} // namespace
+
+SSLSocketAdapter* SSLSocketAdapter::Create(AsyncSocket* socket) {
+ return new SSLSocketAdapter(socket);
+}
+
+SSLSocketAdapter::SSLSocketAdapter(AsyncSocket* socket)
+ : SSLAdapter(socket),
+ ignore_bad_cert_(false),
+ ALLOW_THIS_IN_INITIALIZER_LIST(
+ connected_callback_(this, &SSLSocketAdapter::OnConnected)),
+ ALLOW_THIS_IN_INITIALIZER_LIST(
+ read_callback_(this, &SSLSocketAdapter::OnRead)),
+ ALLOW_THIS_IN_INITIALIZER_LIST(
+ write_callback_(this, &SSLSocketAdapter::OnWrite)),
+ ssl_state_(SSLSTATE_NONE),
+ read_state_(IOSTATE_NONE),
+ write_state_(IOSTATE_NONE) {
+ transport_socket_ = new TransportSocket(socket, this);
+}
+
+SSLSocketAdapter::~SSLSocketAdapter() {
+}
+
+int SSLSocketAdapter::StartSSL(const char* hostname, bool restartable) {
+ DCHECK(!restartable);
+ hostname_ = hostname;
+
+ if (socket_->GetState() != Socket::CS_CONNECTED) {
+ ssl_state_ = SSLSTATE_WAIT;
+ return 0;
+ } else {
+ return BeginSSL();
+ }
+}
+
+int SSLSocketAdapter::BeginSSL() {
+ if (!MessageLoop::current()) {
+ // Certificate verification is done via the Chrome message loop.
+ // Without this check, if we don't have a chrome message loop the
+ // SSL connection just hangs silently.
+ LOG(DFATAL) << "Chrome message loop (needed by SSL certificate "
+ << "verification) does not exist";
+ return net::ERR_UNEXPECTED;
+ }
+
+ // SSLConfigService is not thread-safe, and the default values for SSLConfig
+ // are correct for us, so we don't use the config service to initialize this
+ // object.
+ net::SSLConfig ssl_config;
+ transport_socket_->set_addr(talk_base::SocketAddress(hostname_, 0));
+ ssl_socket_.reset(
+ net::ClientSocketFactory::GetDefaultFactory()->CreateSSLClientSocket(
+ transport_socket_, hostname_.c_str(), ssl_config));
+
+ int result = ssl_socket_->Connect(&connected_callback_);
+
+ if (result == net::ERR_IO_PENDING || result == net::OK) {
+ return 0;
+ } else {
+ LOG(ERROR) << "Could not start SSL: " << net::ErrorToString(result);
+ return result;
+ }
+}
+
+int SSLSocketAdapter::Send(const void* buf, size_t len) {
+ if (ssl_state_ != SSLSTATE_CONNECTED) {
+ return AsyncSocketAdapter::Send(buf, len);
+ } else {
+ scoped_refptr<net::IOBuffer> transport_buf = new net::IOBuffer(len);
+ memcpy(transport_buf->data(), buf, len);
+
+ int result = ssl_socket_->Write(transport_buf, len, NULL);
+ if (result == net::ERR_IO_PENDING) {
+ SetError(EWOULDBLOCK);
+ }
+ transport_buf = NULL;
+ return result;
+ }
+}
+
+int SSLSocketAdapter::Recv(void* buf, size_t len) {
+ switch (ssl_state_) {
+ case SSLSTATE_NONE:
+ return AsyncSocketAdapter::Recv(buf, len);
+
+ case SSLSTATE_WAIT:
+ SetError(EWOULDBLOCK);
+ return -1;
+
+ case SSLSTATE_CONNECTED:
+ switch (read_state_) {
+ case IOSTATE_NONE: {
+ transport_buf_ = new net::IOBuffer(len);
+ int result = ssl_socket_->Read(transport_buf_, len, &read_callback_);
+ if (result >= 0) {
+ memcpy(buf, transport_buf_->data(), len);
+ }
+
+ if (result == net::ERR_IO_PENDING) {
+ read_state_ = IOSTATE_PENDING;
+ SetError(EWOULDBLOCK);
+ } else {
+ if (result < 0) {
+ SetError(result);
+ LOG(INFO) << "Socket error " << result;
+ }
+ transport_buf_ = NULL;
+ }
+ return result;
+ }
+ case IOSTATE_PENDING:
+ SetError(EWOULDBLOCK);
+ return -1;
+
+ case IOSTATE_COMPLETE:
+ memcpy(buf, transport_buf_->data(), len);
+ transport_buf_ = NULL;
+ read_state_ = IOSTATE_NONE;
+ return data_transferred_;
+ }
+ }
+
+ NOTREACHED();
+ return -1;
+}
+
+void SSLSocketAdapter::OnConnected(int result) {
+ if (result == net::OK) {
+ ssl_state_ = SSLSTATE_CONNECTED;
+ OnConnectEvent(this);
+ } else {
+ LOG(WARNING) << "OnConnected failed with error " << result;
+ }
+}
+
+void SSLSocketAdapter::OnRead(int result) {
+ DCHECK(read_state_ == IOSTATE_PENDING);
+ read_state_ = IOSTATE_COMPLETE;
+ data_transferred_ = result;
+ AsyncSocketAdapter::OnReadEvent(this);
+}
+
+void SSLSocketAdapter::OnWrite(int result) {
+ DCHECK(write_state_ == IOSTATE_PENDING);
+ write_state_ = IOSTATE_COMPLETE;
+ data_transferred_ = result;
+ AsyncSocketAdapter::OnWriteEvent(this);
+}
+
+void SSLSocketAdapter::OnConnectEvent(talk_base::AsyncSocket* socket) {
+ if (ssl_state_ != SSLSTATE_WAIT) {
+ AsyncSocketAdapter::OnConnectEvent(socket);
+ } else {
+ ssl_state_ = SSLSTATE_NONE;
+ int result = BeginSSL();
+ if (0 != result) {
+ // TODO(zork): Handle this case gracefully.
+ LOG(WARNING) << "BeginSSL() failed with " << result;
+ }
+ }
+}
+
+TransportSocket::TransportSocket(talk_base::AsyncSocket* socket,
+ SSLSocketAdapter *ssl_adapter)
+ : read_callback_(NULL),
+ write_callback_(NULL),
+ read_buffer_len_(0),
+ write_buffer_len_(0),
+ socket_(socket),
+ was_used_to_convey_data_(false) {
+ socket_->SignalReadEvent.connect(this, &TransportSocket::OnReadEvent);
+ socket_->SignalWriteEvent.connect(this, &TransportSocket::OnWriteEvent);
+}
+
+TransportSocket::~TransportSocket() {
+}
+
+int TransportSocket::Connect(net::CompletionCallback* callback) {
+ // Connect is never called by SSLClientSocket, instead SSLSocketAdapter
+ // calls Connect() on socket_ directly.
+ NOTREACHED();
+ return false;
+}
+
+void TransportSocket::Disconnect() {
+ socket_->Close();
+}
+
+bool TransportSocket::IsConnected() const {
+ return (socket_->GetState() == talk_base::Socket::CS_CONNECTED);
+}
+
+bool TransportSocket::IsConnectedAndIdle() const {
+ // Not implemented.
+ NOTREACHED();
+ return false;
+}
+
+int TransportSocket::GetPeerAddress(net::AddressList* address) const {
+ talk_base::SocketAddress socket_address = socket_->GetRemoteAddress();
+
+ // libjingle supports only IPv4 addresses.
+ sockaddr_in ipv4addr;
+ socket_address.ToSockAddr(&ipv4addr);
+
+ struct addrinfo ai;
+ memset(&ai, 0, sizeof(ai));
+ ai.ai_family = ipv4addr.sin_family;
+ ai.ai_socktype = SOCK_STREAM;
+ ai.ai_protocol = IPPROTO_TCP;
+ ai.ai_addr = reinterpret_cast<struct sockaddr*>(&ipv4addr);
+ ai.ai_addrlen = sizeof(ipv4addr);
+
+ address->Copy(&ai, false);
+ return net::OK;
+}
+
+void TransportSocket::SetSubresourceSpeculation() {
+ NOTREACHED();
+}
+
+void TransportSocket::SetOmniboxSpeculation() {
+ NOTREACHED();
+}
+
+bool TransportSocket::WasEverUsed() const {
+ // We don't use this in ClientSocketPools, so this should never be used.
+ NOTREACHED();
+ return was_used_to_convey_data_;
+}
+
+int TransportSocket::Read(net::IOBuffer* buf, int buf_len,
+ net::CompletionCallback* callback) {
+ DCHECK(buf);
+ DCHECK(!read_callback_);
+ DCHECK(!read_buffer_.get());
+ int result = socket_->Recv(buf->data(), buf_len);
+ if (result < 0) {
+ result = MapPosixError(socket_->GetError());
+ if (result == net::ERR_IO_PENDING) {
+ read_callback_ = callback;
+ read_buffer_ = buf;
+ read_buffer_len_ = buf_len;
+ }
+ }
+ if (result != net::ERR_IO_PENDING)
+ was_used_to_convey_data_ = true;
+ return result;
+}
+
+int TransportSocket::Write(net::IOBuffer* buf, int buf_len,
+ net::CompletionCallback* callback) {
+ DCHECK(buf);
+ DCHECK(!write_callback_);
+ DCHECK(!write_buffer_.get());
+ int result = socket_->Send(buf->data(), buf_len);
+ if (result < 0) {
+ result = MapPosixError(socket_->GetError());
+ if (result == net::ERR_IO_PENDING) {
+ write_callback_ = callback;
+ write_buffer_ = buf;
+ write_buffer_len_ = buf_len;
+ }
+ }
+ if (result != net::ERR_IO_PENDING)
+ was_used_to_convey_data_ = true;
+ return result;
+}
+
+bool TransportSocket::SetReceiveBufferSize(int32 size) {
+ // Not implemented.
+ return false;
+}
+
+bool TransportSocket::SetSendBufferSize(int32 size) {
+ // Not implemented.
+ return false;
+}
+
+void TransportSocket::OnReadEvent(talk_base::AsyncSocket* socket) {
+ if (read_callback_) {
+ DCHECK(read_buffer_.get());
+ net::CompletionCallback* callback = read_callback_;
+ scoped_refptr<net::IOBuffer> buffer = read_buffer_;
+ int buffer_len = read_buffer_len_;
+
+ read_callback_ = NULL;
+ read_buffer_ = NULL;
+ read_buffer_len_ = 0;
+
+ int result = socket_->Recv(buffer->data(), buffer_len);
+ if (result < 0) {
+ result = MapPosixError(socket_->GetError());
+ if (result == net::ERR_IO_PENDING) {
+ read_callback_ = callback;
+ read_buffer_ = buffer;
+ read_buffer_len_ = buffer_len;
+ return;
+ }
+ }
+ was_used_to_convey_data_ = true;
+ callback->RunWithParams(Tuple1<int>(result));
+ }
+}
+
+void TransportSocket::OnWriteEvent(talk_base::AsyncSocket* socket) {
+ if (write_callback_) {
+ DCHECK(write_buffer_.get());
+ net::CompletionCallback* callback = write_callback_;
+ scoped_refptr<net::IOBuffer> buffer = write_buffer_;
+ int buffer_len = write_buffer_len_;
+
+ write_callback_ = NULL;
+ write_buffer_ = NULL;
+ write_buffer_len_ = 0;
+
+ int result = socket_->Send(buffer->data(), buffer_len);
+ if (result < 0) {
+ result = MapPosixError(socket_->GetError());
+ if (result == net::ERR_IO_PENDING) {
+ write_callback_ = callback;
+ write_buffer_ = buffer;
+ write_buffer_len_ = buffer_len;
+ return;
+ }
+ }
+ was_used_to_convey_data_ = true;
+ callback->RunWithParams(Tuple1<int>(result));
+ }
+}
+
+} // namespace notifier
diff --git a/jingle/notifier/communicator/ssl_socket_adapter.h b/jingle/notifier/communicator/ssl_socket_adapter.h
new file mode 100644
index 0000000..cdbc94e
--- /dev/null
+++ b/jingle/notifier/communicator/ssl_socket_adapter.h
@@ -0,0 +1,145 @@
+// Copyright (c) 2010 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 JINGLE_NOTIFIER_COMMUNICATOR_SSL_SOCKET_ADAPTER_H_
+#define JINGLE_NOTIFIER_COMMUNICATOR_SSL_SOCKET_ADAPTER_H_
+
+#include "base/scoped_ptr.h"
+#include "net/base/completion_callback.h"
+#include "net/base/io_buffer.h"
+#include "net/base/net_errors.h"
+#include "net/base/net_log.h"
+#include "net/socket/client_socket.h"
+#include "net/socket/ssl_client_socket.h"
+#include "talk/base/asyncsocket.h"
+#include "talk/base/ssladapter.h"
+
+namespace notifier {
+
+class SSLSocketAdapter;
+
+// TODO(sergeyu): Write unittests for this code!
+
+// This class provides a wrapper to libjingle's talk_base::AsyncSocket that
+// implements Chromium's net::ClientSocket interface. It's used by
+// SSLSocketAdapter to enable Chromium's SSL implementation to work over
+// libjingle's socket class.
+class TransportSocket : public net::ClientSocket, public sigslot::has_slots<> {
+ public:
+ TransportSocket(talk_base::AsyncSocket* socket,
+ SSLSocketAdapter *ssl_adapter);
+ ~TransportSocket();
+
+ void set_addr(const talk_base::SocketAddress& addr) {
+ addr_ = addr;
+ }
+
+ // net::ClientSocket implementation
+
+ virtual int Connect(net::CompletionCallback* callback);
+ virtual void Disconnect();
+ virtual bool IsConnected() const;
+ virtual bool IsConnectedAndIdle() const;
+ virtual int GetPeerAddress(net::AddressList* address) const;
+ virtual const net::BoundNetLog& NetLog() const { return net_log_; }
+ virtual void SetSubresourceSpeculation();
+ virtual void SetOmniboxSpeculation();
+ virtual bool WasEverUsed() const;
+
+ // net::Socket implementation
+
+ virtual int Read(net::IOBuffer* buf, int buf_len,
+ net::CompletionCallback* callback);
+ virtual int Write(net::IOBuffer* buf, int buf_len,
+ net::CompletionCallback* callback);
+ virtual bool SetReceiveBufferSize(int32 size);
+ virtual bool SetSendBufferSize(int32 size);
+
+ private:
+ friend class SSLSocketAdapter;
+
+ void OnReadEvent(talk_base::AsyncSocket* socket);
+ void OnWriteEvent(talk_base::AsyncSocket* socket);
+
+ net::CompletionCallback* read_callback_;
+ net::CompletionCallback* write_callback_;
+
+ scoped_refptr<net::IOBuffer> read_buffer_;
+ int read_buffer_len_;
+ scoped_refptr<net::IOBuffer> write_buffer_;
+ int write_buffer_len_;
+
+ net::BoundNetLog net_log_;
+
+ talk_base::AsyncSocket *socket_;
+ talk_base::SocketAddress addr_;
+
+ bool was_used_to_convey_data_;
+
+ DISALLOW_COPY_AND_ASSIGN(TransportSocket);
+};
+
+// This provides a talk_base::AsyncSocketAdapter interface around Chromium's
+// net::SSLClientSocket class. This allows notifier to use Chromium's SSL
+// implementation instead of OpenSSL.
+class SSLSocketAdapter : public talk_base::SSLAdapter {
+ public:
+ explicit SSLSocketAdapter(talk_base::AsyncSocket* socket);
+ ~SSLSocketAdapter();
+
+ // StartSSL returns 0 if successful, or non-zero on failure.
+ // If StartSSL is called while the socket is closed or connecting, the SSL
+ // negotiation will begin as soon as the socket connects.
+ //
+ // restartable is not implemented, and must be set to false.
+ virtual int StartSSL(const char* hostname, bool restartable);
+
+ // Create the default SSL adapter for this platform.
+ static SSLSocketAdapter* Create(AsyncSocket* socket);
+
+ virtual int Send(const void* pv, size_t cb);
+ virtual int Recv(void* pv, size_t cb);
+
+ private:
+ friend class TransportSocket;
+
+ enum SSLState {
+ SSLSTATE_NONE,
+ SSLSTATE_WAIT,
+ SSLSTATE_CONNECTED,
+ };
+
+ enum IOState {
+ IOSTATE_NONE,
+ IOSTATE_PENDING,
+ IOSTATE_COMPLETE,
+ };
+
+ void OnConnected(int result);
+ void OnRead(int result);
+ void OnWrite(int result);
+
+ virtual void OnConnectEvent(talk_base::AsyncSocket* socket);
+
+ int BeginSSL();
+
+ bool ignore_bad_cert_;
+ std::string hostname_;
+ TransportSocket* transport_socket_;
+ scoped_ptr<net::SSLClientSocket> ssl_socket_;
+ net::CompletionCallbackImpl<SSLSocketAdapter> connected_callback_;
+ net::CompletionCallbackImpl<SSLSocketAdapter> read_callback_;
+ net::CompletionCallbackImpl<SSLSocketAdapter> write_callback_;
+ SSLState ssl_state_;
+ IOState read_state_;
+ IOState write_state_;
+ scoped_refptr<net::IOBuffer> transport_buf_;
+ int data_transferred_;
+
+ DISALLOW_COPY_AND_ASSIGN(SSLSocketAdapter);
+};
+
+} // namespace notifier
+
+#endif // JINGLE_NOTIFIER_COMMUNICATOR_SSL_SOCKET_ADAPTER_H_
diff --git a/jingle/notifier/communicator/xmpp_connection_generator.cc b/jingle/notifier/communicator/xmpp_connection_generator.cc
index 25b131c..ad551d2 100644
--- a/jingle/notifier/communicator/xmpp_connection_generator.cc
+++ b/jingle/notifier/communicator/xmpp_connection_generator.cc
@@ -23,8 +23,10 @@
#include "base/callback.h"
#include "base/compiler_specific.h"
#include "base/logging.h"
+#include "jingle/notifier/base/signal_thread_task.h"
#include "jingle/notifier/communicator/connection_options.h"
#include "jingle/notifier/communicator/connection_settings.h"
+#include "jingle/notifier/communicator/product_info.h"
#include "net/base/net_errors.h"
#include "net/base/sys_addrinfo.h"
#include "talk/base/httpcommon-inl.h"
diff --git a/jingle/notifier/communicator/xmpp_socket_adapter.cc b/jingle/notifier/communicator/xmpp_socket_adapter.cc
new file mode 100644
index 0000000..7d7a2e9
--- /dev/null
+++ b/jingle/notifier/communicator/xmpp_socket_adapter.cc
@@ -0,0 +1,429 @@
+// 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 "jingle/notifier/communicator/xmpp_socket_adapter.h"
+
+#include <iomanip>
+#include <string>
+
+#include "base/logging.h"
+#include "jingle/notifier/base/ssl_adapter.h"
+#include "jingle/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/base/thread.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());
+ 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(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 (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.
+ }
+
+#if defined(FEATURE_ENABLE_SSL)
+ talk_base::SSLAdapter* ssl_adapter = notifier::CreateSSLAdapter(socket);
+ socket = ssl_adapter; // For our purposes the SSL adapter is the socket.
+#endif
+
+ 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;
+ }
+
+ DCHECK(socket_);
+
+ 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;
+ }
+
+ DCHECK(socket_);
+
+ 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(sync): 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(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(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(INFO) << "XmppSocketAdapter::OnConnectEvent - STATE_OPEN";
+ SignalConnected();
+#if defined(FEATURE_ENABLE_SSL)
+ } else if (state_ == STATE_TLS_CONNECTING) {
+ state_ = STATE_TLS_OPEN;
+ LOG(INFO) << "XmppSocketAdapter::OnConnectEvent - STATE_TLS_OPEN";
+ SignalSSLConnected();
+ if (write_buffer_length_ > 0) {
+ HandleWritable();
+ }
+#endif // defined(FEATURE_ENABLE_SSL)
+ } else {
+ LOG(DFATAL) << "unexpected XmppSocketAdapter::OnConnectEvent state: "
+ << state_;
+ }
+}
+
+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(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;
+
+ DCHECK_EQ(write_buffer_length_, 0U);
+
+ 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];
+ DCHECK_LE(write_buffer_length_, 64000U);
+ 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) {
+ DCHECK(error);
+ DCHECK(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/jingle/notifier/communicator/xmpp_socket_adapter.h b/jingle/notifier/communicator/xmpp_socket_adapter.h
new file mode 100644
index 0000000..ff22a9c
--- /dev/null
+++ b/jingle/notifier/communicator/xmpp_socket_adapter.h
@@ -0,0 +1,87 @@
+// 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 JINGLE_NOTIFIER_COMMUNICATOR_XMPP_SOCKET_ADAPTER_H_
+#define JINGLE_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 // JINGLE_NOTIFIER_COMMUNICATOR_XMPP_SOCKET_ADAPTER_H_
diff --git a/jingle/notifier/listener/mediator_thread_impl.cc b/jingle/notifier/listener/mediator_thread_impl.cc
index 918b49b..02d48fb1 100644
--- a/jingle/notifier/listener/mediator_thread_impl.cc
+++ b/jingle/notifier/listener/mediator_thread_impl.cc
@@ -16,6 +16,8 @@
#include "jingle/notifier/listener/subscribe_task.h"
#include "net/base/host_port_pair.h"
#include "net/base/host_resolver.h"
+#include "talk/base/physicalsocketserver.h"
+#include "talk/base/thread.h"
#include "talk/xmpp/xmppclient.h"
#include "talk/xmpp/xmppclientsettings.h"
@@ -49,6 +51,44 @@ void MediatorThreadImpl::Start() {
// TODO(akalin): Make this function return a bool and remove this
// CHECK().
CHECK(worker_thread_.StartWithOptions(options));
+ if (!notifier_options_.use_chrome_async_socket) {
+ worker_message_loop()->PostTask(
+ FROM_HERE,
+ NewRunnableMethod(this, &MediatorThreadImpl::StartLibjingleThread));
+ }
+}
+
+void MediatorThreadImpl::StartLibjingleThread() {
+ DCHECK_EQ(MessageLoop::current(), worker_message_loop());
+ DCHECK(!notifier_options_.use_chrome_async_socket);
+ socket_server_.reset(new talk_base::PhysicalSocketServer());
+ libjingle_thread_.reset(new talk_base::Thread());
+ talk_base::ThreadManager::SetCurrent(libjingle_thread_.get());
+ worker_message_loop()->PostTask(
+ FROM_HERE,
+ NewRunnableMethod(this, &MediatorThreadImpl::PumpLibjingleLoop));
+}
+
+void MediatorThreadImpl::StopLibjingleThread() {
+ DCHECK_EQ(MessageLoop::current(), worker_message_loop());
+ DCHECK(!notifier_options_.use_chrome_async_socket);
+ talk_base::ThreadManager::SetCurrent(NULL);
+ libjingle_thread_.reset();
+ socket_server_.reset();
+}
+
+void MediatorThreadImpl::PumpLibjingleLoop() {
+ DCHECK_EQ(MessageLoop::current(), worker_message_loop());
+ DCHECK(!notifier_options_.use_chrome_async_socket);
+ // Pump the libjingle message loop 100ms at a time.
+ if (!libjingle_thread_.get()) {
+ // StopLibjingleThread() was called.
+ return;
+ }
+ libjingle_thread_->ProcessMessages(100);
+ worker_message_loop()->PostTask(
+ FROM_HERE,
+ NewRunnableMethod(this, &MediatorThreadImpl::PumpLibjingleLoop));
}
void MediatorThreadImpl::Login(const buzz::XmppClientSettings& settings) {
@@ -63,6 +103,11 @@ void MediatorThreadImpl::Logout() {
worker_message_loop()->PostTask(
FROM_HERE,
NewRunnableMethod(this, &MediatorThreadImpl::DoDisconnect));
+ if (!notifier_options_.use_chrome_async_socket) {
+ worker_message_loop()->PostTask(
+ FROM_HERE,
+ NewRunnableMethod(this, &MediatorThreadImpl::StopLibjingleThread));
+ }
// TODO(akalin): Decomp this into a separate stop method.
worker_thread_.Stop();
// Process any messages the worker thread may be posted on our
@@ -160,6 +205,7 @@ void MediatorThreadImpl::DoLogin(
// Language is not used in the stanza so we default to |en|.
std::string lang = "en";
login_.reset(new notifier::Login(pump_.get(),
+ notifier_options_.use_chrome_async_socket,
settings,
options,
lang,
diff --git a/jingle/notifier/listener/mediator_thread_impl.h b/jingle/notifier/listener/mediator_thread_impl.h
index 342079f..755731e 100644
--- a/jingle/notifier/listener/mediator_thread_impl.h
+++ b/jingle/notifier/listener/mediator_thread_impl.h
@@ -93,6 +93,10 @@ class MediatorThreadImpl
MessageLoop* parent_message_loop_;
private:
+ void StartLibjingleThread();
+ void PumpLibjingleLoop();
+ void StopLibjingleThread();
+
// Called from within the thread on internal events.
void DoLogin(const buzz::XmppClientSettings& settings);
void DoDisconnect();
@@ -133,6 +137,10 @@ class MediatorThreadImpl
scoped_ptr<notifier::TaskPump> pump_;
scoped_ptr<notifier::Login> login_;
+ // Used only when |use_chrome_async_socket_| is false.
+ scoped_ptr<talk_base::SocketServer> socket_server_;
+ scoped_ptr<talk_base::Thread> libjingle_thread_;
+
DISALLOW_COPY_AND_ASSIGN(MediatorThreadImpl);
};