| 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". | 
|  |