| Index: jingle/notifier/communicator/single_login_attempt.cc
|
| diff --git a/jingle/notifier/communicator/single_login_attempt.cc b/jingle/notifier/communicator/single_login_attempt.cc
|
| index 4db7758bfc0403b4be3e8300ea97754208c4259c..ccdf0ce81865b54144548a1de7602cd81f325412 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".
|
|
|