| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include <string> | |
| 6 | |
| 7 #include "chrome/browser/sync/notifier/communicator/login.h" | |
| 8 | |
| 9 #include "chrome/browser/sync/notifier/base/network_status_detector_task.h" | |
| 10 #include "chrome/browser/sync/notifier/base/time.h" | |
| 11 #include "chrome/browser/sync/notifier/base/timer.h" | |
| 12 #include "chrome/browser/sync/notifier/communicator/auto_reconnect.h" | |
| 13 #include "chrome/browser/sync/notifier/communicator/connection_options.h" | |
| 14 #include "chrome/browser/sync/notifier/communicator/login_settings.h" | |
| 15 #include "chrome/browser/sync/notifier/communicator/product_info.h" | |
| 16 #include "chrome/browser/sync/notifier/communicator/single_login_attempt.h" | |
| 17 #include "talk/base/common.h" | |
| 18 #include "talk/base/firewallsocketserver.h" | |
| 19 #include "talk/base/logging.h" | |
| 20 #include "talk/base/taskrunner.h" | |
| 21 #include "talk/xmllite/xmlelement.h" | |
| 22 #include "talk/xmpp/asyncsocket.h" | |
| 23 #include "talk/xmpp/prexmppauth.h" | |
| 24 #include "talk/xmpp/xmppclient.h" | |
| 25 #include "talk/xmpp/xmppclientsettings.h" | |
| 26 #include "talk/xmpp/xmppengine.h" | |
| 27 | |
| 28 namespace notifier { | |
| 29 | |
| 30 // Redirect valid for 5 minutes. | |
| 31 static const time64 kRedirectTimeoutNs = 5 * kMinsTo100ns; | |
| 32 | |
| 33 // Disconnect if network stays down for more than 10 seconds. | |
| 34 static const int kDisconnectionDelaySecs = 10; | |
| 35 | |
| 36 Login::Login(talk_base::Task* parent, | |
| 37 const buzz::XmppClientSettings& user_settings, | |
| 38 const ConnectionOptions& options, | |
| 39 std::string lang, | |
| 40 ServerInformation* server_list, | |
| 41 int server_count, | |
| 42 NetworkStatusDetectorTask* network_status, | |
| 43 talk_base::FirewallManager* firewall, | |
| 44 bool proxy_only, | |
| 45 bool previous_login_successful) | |
| 46 : login_settings_(new LoginSettings(user_settings, | |
| 47 options, | |
| 48 lang, | |
| 49 server_list, | |
| 50 server_count, | |
| 51 firewall, | |
| 52 proxy_only)), | |
| 53 single_attempt_(NULL), | |
| 54 successful_connection_(previous_login_successful), | |
| 55 parent_(parent), | |
| 56 state_(STATE_OPENING), | |
| 57 redirect_time_ns_(0), | |
| 58 redirect_port_(0), | |
| 59 unexpected_disconnect_occurred_(false), | |
| 60 reset_unexpected_timer_(NULL), | |
| 61 google_host_(user_settings.host()), | |
| 62 google_user_(user_settings.user()), | |
| 63 disconnect_timer_(NULL) { | |
| 64 if (!network_status) { | |
| 65 network_status = NetworkStatusDetectorTask::Create(parent_); | |
| 66 if (network_status) { | |
| 67 // On linux we don't have an implementation of NetworkStatusDetectorTask. | |
| 68 network_status->Start(); | |
| 69 } | |
| 70 } | |
| 71 | |
| 72 if (network_status) { | |
| 73 network_status->SignalNetworkStateDetected.connect( | |
| 74 this, &Login::OnNetworkStateDetected); | |
| 75 auto_reconnect_.reset(new AutoReconnect(parent_, network_status)); | |
| 76 auto_reconnect_->SignalStartConnection.connect(this, | |
| 77 &Login::StartConnection); | |
| 78 auto_reconnect_->SignalTimerStartStop.connect( | |
| 79 this, | |
| 80 &Login::OnAutoReconnectTimerChange); | |
| 81 SignalClientStateChange.connect(auto_reconnect_.get(), | |
| 82 &AutoReconnect::OnClientStateChange); | |
| 83 SignalIdleChange.connect(auto_reconnect_.get(), | |
| 84 &AutoReconnect::set_idle); | |
| 85 SignalPowerSuspended.connect(auto_reconnect_.get(), | |
| 86 &AutoReconnect::OnPowerSuspend); | |
| 87 } | |
| 88 } | |
| 89 | |
| 90 // Defined so that the destructors are executed here (and the corresponding | |
| 91 // classes don't need to be included in the header file). | |
| 92 Login::~Login() { | |
| 93 if (single_attempt_) { | |
| 94 single_attempt_->Abort(); | |
| 95 single_attempt_ = NULL; | |
| 96 } | |
| 97 } | |
| 98 | |
| 99 void Login::StartConnection() { | |
| 100 // If there is a server redirect, use it. | |
| 101 if (GetCurrent100NSTime() < redirect_time_ns_ + kRedirectTimeoutNs) { | |
| 102 // Override server/port with redirect values. | |
| 103 talk_base::SocketAddress server_override; | |
| 104 server_override.SetIP(redirect_server_, false); | |
| 105 ASSERT(redirect_port_ != 0); | |
| 106 server_override.SetPort(redirect_port_); | |
| 107 login_settings_->set_server_override(server_override); | |
| 108 } else { | |
| 109 login_settings_->clear_server_override(); | |
| 110 } | |
| 111 | |
| 112 if (single_attempt_) { | |
| 113 single_attempt_->Abort(); | |
| 114 single_attempt_ = NULL; | |
| 115 } | |
| 116 single_attempt_ = new SingleLoginAttempt(parent_, | |
| 117 login_settings_.get(), | |
| 118 successful_connection_); | |
| 119 | |
| 120 // Do the signaling hook-ups. | |
| 121 single_attempt_->SignalLoginFailure.connect(this, &Login::OnLoginFailure); | |
| 122 single_attempt_->SignalRedirect.connect(this, &Login::OnRedirect); | |
| 123 single_attempt_->SignalClientStateChange.connect( | |
| 124 this, | |
| 125 &Login::OnClientStateChange); | |
| 126 single_attempt_->SignalUnexpectedDisconnect.connect( | |
| 127 this, | |
| 128 &Login::OnUnexpectedDisconnect); | |
| 129 single_attempt_->SignalLogoff.connect( | |
| 130 this, | |
| 131 &Login::OnLogoff); | |
| 132 single_attempt_->SignalNeedAutoReconnect.connect( | |
| 133 this, | |
| 134 &Login::DoAutoReconnect); | |
| 135 SignalLogInput.repeat(single_attempt_->SignalLogInput); | |
| 136 SignalLogOutput.repeat(single_attempt_->SignalLogOutput); | |
| 137 | |
| 138 single_attempt_->Start(); | |
| 139 } | |
| 140 | |
| 141 const std::string& Login::google_host() const { | |
| 142 return google_host_; | |
| 143 } | |
| 144 | |
| 145 const std::string& Login::google_user() const { | |
| 146 return google_user_; | |
| 147 } | |
| 148 | |
| 149 const talk_base::ProxyInfo& Login::proxy() const { | |
| 150 return proxy_info_; | |
| 151 } | |
| 152 | |
| 153 void Login::OnLoginFailure(const LoginFailure& failure) { | |
| 154 auto_reconnect_->StopReconnectTimer(); | |
| 155 HandleClientStateChange(STATE_CLOSED); | |
| 156 SignalLoginFailure(failure); | |
| 157 } | |
| 158 | |
| 159 void Login::OnLogoff() { | |
| 160 HandleClientStateChange(STATE_CLOSED); | |
| 161 } | |
| 162 | |
| 163 void Login::OnClientStateChange(buzz::XmppEngine::State state) { | |
| 164 ConnectionState new_state = STATE_CLOSED; | |
| 165 | |
| 166 switch (state) { | |
| 167 case buzz::XmppEngine::STATE_NONE: | |
| 168 case buzz::XmppEngine::STATE_CLOSED: | |
| 169 // Ignore the closed state (because we may be trying the next dns entry). | |
| 170 // | |
| 171 // But we go to this state for other signals when there is no retry | |
| 172 // happening. | |
| 173 new_state = state_; | |
| 174 break; | |
| 175 | |
| 176 case buzz::XmppEngine::STATE_START: | |
| 177 case buzz::XmppEngine::STATE_OPENING: | |
| 178 new_state = STATE_OPENING; | |
| 179 break; | |
| 180 | |
| 181 case buzz::XmppEngine::STATE_OPEN: | |
| 182 new_state = STATE_OPENED; | |
| 183 break; | |
| 184 | |
| 185 default: | |
| 186 ASSERT(false); | |
| 187 break; | |
| 188 } | |
| 189 HandleClientStateChange(new_state); | |
| 190 } | |
| 191 | |
| 192 void Login::HandleClientStateChange(ConnectionState new_state) { | |
| 193 // Do we need to transition between the retrying and closed states? | |
| 194 if (auto_reconnect_.get() && auto_reconnect_->is_retrying()) { | |
| 195 if (new_state == STATE_CLOSED) { | |
| 196 new_state = STATE_RETRYING; | |
| 197 } | |
| 198 } else { | |
| 199 if (new_state == STATE_RETRYING) { | |
| 200 new_state = STATE_CLOSED; | |
| 201 } | |
| 202 } | |
| 203 | |
| 204 if (new_state != state_) { | |
| 205 state_ = new_state; | |
| 206 if (reset_unexpected_timer_) { | |
| 207 reset_unexpected_timer_->Abort(); | |
| 208 reset_unexpected_timer_ = NULL; | |
| 209 } | |
| 210 | |
| 211 if (state_ == STATE_OPENED) { | |
| 212 successful_connection_ = true; | |
| 213 | |
| 214 google_host_ = single_attempt_->xmpp_client()->jid().domain(); | |
| 215 google_user_ = single_attempt_->xmpp_client()->jid().node(); | |
| 216 proxy_info_ = single_attempt_->proxy(); | |
| 217 | |
| 218 reset_unexpected_timer_ = new Timer(parent_, | |
| 219 kResetReconnectInfoDelaySec, | |
| 220 false); // Repeat. | |
| 221 reset_unexpected_timer_->SignalTimeout.connect( | |
| 222 this, | |
| 223 &Login::ResetUnexpectedDisconnect); | |
| 224 } | |
| 225 SignalClientStateChange(state_); | |
| 226 } | |
| 227 } | |
| 228 | |
| 229 void Login::OnAutoReconnectTimerChange() { | |
| 230 if (!single_attempt_ || !single_attempt_->xmpp_client()) { | |
| 231 HandleClientStateChange(STATE_CLOSED); | |
| 232 return; | |
| 233 } | |
| 234 OnClientStateChange(single_attempt_->xmpp_client()->GetState()); | |
| 235 } | |
| 236 | |
| 237 buzz::XmppClient* Login::xmpp_client() { | |
| 238 if (!single_attempt_) { | |
| 239 return NULL; | |
| 240 } | |
| 241 return single_attempt_->xmpp_client(); | |
| 242 } | |
| 243 | |
| 244 int Login::seconds_until_reconnect() const { | |
| 245 return auto_reconnect_->seconds_until(); | |
| 246 } | |
| 247 | |
| 248 void Login::UseNextConnection() { | |
| 249 if (!single_attempt_) { | |
| 250 // Just in case, there is an obscure case that causes this to get called | |
| 251 // when there is no single_attempt_. | |
| 252 return; | |
| 253 } | |
| 254 single_attempt_->UseNextConnection(); | |
| 255 } | |
| 256 | |
| 257 void Login::UseCurrentConnection() { | |
| 258 if (!single_attempt_) { | |
| 259 // Just in case, there is an obscure case that causes this to get called | |
| 260 // when there is no single_attempt_. | |
| 261 return; | |
| 262 } | |
| 263 single_attempt_->UseCurrentConnection(); | |
| 264 } | |
| 265 | |
| 266 void Login::OnRedirect(const std::string& redirect_server, int redirect_port) { | |
| 267 ASSERT(redirect_port_ != 0); | |
| 268 | |
| 269 redirect_time_ns_ = GetCurrent100NSTime(); | |
| 270 redirect_server_ = redirect_server; | |
| 271 redirect_port_ = redirect_port; | |
| 272 | |
| 273 // Drop the current connection, and start the login process again. | |
| 274 StartConnection(); | |
| 275 } | |
| 276 | |
| 277 void Login::OnUnexpectedDisconnect() { | |
| 278 if (reset_unexpected_timer_) { | |
| 279 reset_unexpected_timer_->Abort(); | |
| 280 reset_unexpected_timer_ = NULL; | |
| 281 } | |
| 282 | |
| 283 // Start the login process again. | |
| 284 if (unexpected_disconnect_occurred_) { | |
| 285 // If we already have received an unexpected disconnect recently, then our | |
| 286 // account may have be jailed due to abuse, so we shouldn't make the | |
| 287 // situation worse by trying really hard to reconnect. Instead, we'll do | |
| 288 // the autoreconnect route, which has exponential back-off. | |
| 289 DoAutoReconnect(); | |
| 290 return; | |
| 291 } | |
| 292 StartConnection(); | |
| 293 unexpected_disconnect_occurred_ = true; | |
| 294 } | |
| 295 | |
| 296 void Login::ResetUnexpectedDisconnect() { | |
| 297 reset_unexpected_timer_ = NULL; | |
| 298 unexpected_disconnect_occurred_ = false; | |
| 299 } | |
| 300 | |
| 301 void Login::DoAutoReconnect() { | |
| 302 bool allow_auto_reconnect = | |
| 303 login_settings_->connection_options().auto_reconnect(); | |
| 304 // Start the reconnect time before aborting the connection to ensure that | |
| 305 // AutoReconnect::is_retrying() is true, so that the Login doesn't | |
| 306 // transition to the CLOSED state (which would cause the reconnection timer | |
| 307 // to reset and not double). | |
| 308 if (allow_auto_reconnect) { | |
| 309 auto_reconnect_->StartReconnectTimer(); | |
| 310 } | |
| 311 | |
| 312 if (single_attempt_) { | |
| 313 single_attempt_->Abort(); | |
| 314 single_attempt_ = NULL; | |
| 315 } | |
| 316 | |
| 317 if (!allow_auto_reconnect) { | |
| 318 HandleClientStateChange(STATE_CLOSED); | |
| 319 return; | |
| 320 } | |
| 321 } | |
| 322 | |
| 323 void Login::OnNetworkStateDetected(bool was_alive, bool is_alive) { | |
| 324 if (was_alive && !is_alive) { | |
| 325 // Our network connection just went down. Setup a timer to disconnect. | |
| 326 // Don't disconnect immediately to avoid constant | |
| 327 // connection/disconnection due to flaky network interfaces. | |
| 328 ASSERT(disconnect_timer_ == NULL); | |
| 329 disconnect_timer_ = new Timer(parent_, kDisconnectionDelaySecs, false); | |
| 330 disconnect_timer_->SignalTimeout.connect(this, | |
| 331 &Login::OnDisconnectTimeout); | |
| 332 } else if (!was_alive && is_alive) { | |
| 333 // Our connection has come back up. If we have a disconnect timer going, | |
| 334 // abort it so we don't disconnect. | |
| 335 if (disconnect_timer_) { | |
| 336 disconnect_timer_->Abort(); | |
| 337 // It will free itself. | |
| 338 disconnect_timer_ = NULL; | |
| 339 } | |
| 340 } | |
| 341 } | |
| 342 | |
| 343 void Login::OnDisconnectTimeout() { | |
| 344 disconnect_timer_ = NULL; | |
| 345 | |
| 346 if (state_ != STATE_OPENED) { | |
| 347 return; | |
| 348 } | |
| 349 | |
| 350 if (single_attempt_) { | |
| 351 single_attempt_->Abort(); | |
| 352 single_attempt_ = NULL; | |
| 353 } | |
| 354 | |
| 355 StartConnection(); | |
| 356 } | |
| 357 | |
| 358 } // namespace notifier | |
| OLD | NEW |