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 |