OLD | NEW |
1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include <algorithm> | 5 #include <algorithm> |
6 #include <cstddef> | 6 #include <cstddef> |
7 #include <string> | 7 #include <string> |
8 #include <vector> | 8 #include <vector> |
9 | 9 |
10 #include "jingle/notifier/communicator/single_login_attempt.h" | 10 #include "jingle/notifier/communicator/single_login_attempt.h" |
11 | 11 |
12 #include "base/logging.h" | 12 #include "base/logging.h" |
13 #include "jingle/notifier/base/chrome_async_socket.h" | 13 #include "jingle/notifier/base/chrome_async_socket.h" |
14 #include "jingle/notifier/base/xmpp_client_socket_factory.h" | 14 #include "jingle/notifier/base/xmpp_client_socket_factory.h" |
15 #include "jingle/notifier/communicator/connection_options.h" | 15 #include "jingle/notifier/communicator/connection_options.h" |
16 #include "jingle/notifier/communicator/connection_settings.h" | 16 #include "jingle/notifier/communicator/connection_settings.h" |
17 #include "jingle/notifier/communicator/const_communicator.h" | 17 #include "jingle/notifier/communicator/const_communicator.h" |
18 #include "jingle/notifier/communicator/gaia_token_pre_xmpp_auth.h" | 18 #include "jingle/notifier/communicator/gaia_token_pre_xmpp_auth.h" |
19 #include "jingle/notifier/communicator/login_failure.h" | 19 #include "jingle/notifier/communicator/login_failure.h" |
20 #include "jingle/notifier/communicator/login_settings.h" | 20 #include "jingle/notifier/communicator/login_settings.h" |
21 #include "jingle/notifier/communicator/product_info.h" | |
22 #include "jingle/notifier/communicator/xmpp_connection_generator.h" | 21 #include "jingle/notifier/communicator/xmpp_connection_generator.h" |
23 #include "jingle/notifier/communicator/xmpp_socket_adapter.h" | |
24 #include "net/base/ssl_config_service.h" | 22 #include "net/base/ssl_config_service.h" |
25 #include "net/socket/client_socket_factory.h" | 23 #include "net/socket/client_socket_factory.h" |
26 #include "talk/base/asynchttprequest.h" | |
27 #include "talk/base/firewallsocketserver.h" | |
28 #include "talk/base/signalthread.h" | |
29 #include "talk/base/taskrunner.h" | |
30 #include "talk/base/win32socketinit.h" | |
31 #include "talk/xmllite/xmlelement.h" | 24 #include "talk/xmllite/xmlelement.h" |
32 #include "talk/xmpp/xmppclient.h" | 25 #include "talk/xmpp/xmppclient.h" |
33 #include "talk/xmpp/xmppclientsettings.h" | 26 #include "talk/xmpp/xmppclientsettings.h" |
34 #include "talk/xmpp/constants.h" | 27 #include "talk/xmpp/constants.h" |
35 | 28 |
36 namespace net { | 29 namespace net { |
37 class NetLog; | 30 class NetLog; |
38 } // namespace net | 31 } // namespace net |
39 | 32 |
40 namespace notifier { | 33 namespace notifier { |
(...skipping 13 matching lines...) Expand all Loading... |
54 *stream_error = NULL; | 47 *stream_error = NULL; |
55 if (*error == buzz::XmppEngine::ERROR_STREAM) { | 48 if (*error == buzz::XmppEngine::ERROR_STREAM) { |
56 const buzz::XmlElement* error_element = client->GetStreamError(); | 49 const buzz::XmlElement* error_element = client->GetStreamError(); |
57 if (error_element) { | 50 if (error_element) { |
58 *stream_error = new buzz::XmlElement(*error_element); | 51 *stream_error = new buzz::XmlElement(*error_element); |
59 } | 52 } |
60 } | 53 } |
61 } | 54 } |
62 | 55 |
63 SingleLoginAttempt::SingleLoginAttempt(talk_base::TaskParent* parent, | 56 SingleLoginAttempt::SingleLoginAttempt(talk_base::TaskParent* parent, |
64 LoginSettings* login_settings, | 57 LoginSettings* login_settings) |
65 bool use_chrome_async_socket, | |
66 bool successful_connection) | |
67 : talk_base::Task(parent), | 58 : talk_base::Task(parent), |
68 use_chrome_async_socket_(use_chrome_async_socket), | |
69 state_(buzz::XmppEngine::STATE_NONE), | 59 state_(buzz::XmppEngine::STATE_NONE), |
70 code_(buzz::XmppEngine::ERROR_NONE), | 60 code_(buzz::XmppEngine::ERROR_NONE), |
71 subcode_(0), | 61 subcode_(0), |
72 need_authentication_(false), | 62 need_authentication_(false), |
73 certificate_expired_(false), | 63 certificate_expired_(false), |
74 cookie_refreshed_(false), | |
75 successful_connection_(successful_connection), | |
76 login_settings_(login_settings), | 64 login_settings_(login_settings), |
77 client_(NULL) { | 65 client_(NULL) { |
78 #if defined(OS_WIN) | |
79 talk_base::EnsureWinsockInit(); | |
80 #endif | |
81 connection_generator_.reset(new XmppConnectionGenerator( | 66 connection_generator_.reset(new XmppConnectionGenerator( |
82 this, | 67 this, |
83 login_settings_->host_resolver(), | 68 login_settings_->host_resolver(), |
84 &login_settings_->connection_options(), | 69 &login_settings_->connection_options(), |
85 login_settings_->try_ssltcp_first(), | 70 login_settings_->try_ssltcp_first(), |
86 login_settings_->proxy_only(), | 71 login_settings_->proxy_only(), |
87 login_settings_->server_list(), | 72 login_settings_->server_list(), |
88 login_settings_->server_count())); | 73 login_settings_->server_count())); |
89 | 74 |
90 connection_generator_->SignalExhaustedSettings.connect( | 75 connection_generator_->SignalExhaustedSettings.connect( |
(...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
147 return; | 132 return; |
148 } | 133 } |
149 | 134 |
150 if (!successfully_resolved_dns) { | 135 if (!successfully_resolved_dns) { |
151 code_ = buzz::XmppEngine::ERROR_SOCKET; | 136 code_ = buzz::XmppEngine::ERROR_SOCKET; |
152 subcode_ = first_dns_error; | 137 subcode_ = first_dns_error; |
153 } | 138 } |
154 | 139 |
155 LOG(INFO) << "Connection failed with error " << code_; | 140 LOG(INFO) << "Connection failed with error " << code_; |
156 | 141 |
157 // We were connected and we had a problem. | 142 SignalNeedAutoReconnect(); |
158 if (successful_connection_) { | |
159 SignalNeedAutoReconnect(); | |
160 // Expect to be deleted at this point. | |
161 return; | |
162 } | |
163 | |
164 DiagnoseConnectionError(); | |
165 } | 143 } |
166 | 144 |
167 void SingleLoginAttempt::UseNextConnection() { | 145 void SingleLoginAttempt::UseNextConnection() { |
168 DCHECK(connection_generator_.get()); | 146 DCHECK(connection_generator_.get()); |
169 ClearClient(); | 147 ClearClient(); |
170 connection_generator_->UseNextConnection(); | 148 connection_generator_->UseNextConnection(); |
171 } | 149 } |
172 | 150 |
173 void SingleLoginAttempt::UseCurrentConnection() { | 151 void SingleLoginAttempt::UseCurrentConnection() { |
174 DCHECK(connection_generator_.get()); | 152 DCHECK(connection_generator_.get()); |
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
218 need_authentication_ = true; | 196 need_authentication_ = true; |
219 } | 197 } |
220 | 198 |
221 void SingleLoginAttempt::OnCertificateExpired() { | 199 void SingleLoginAttempt::OnCertificateExpired() { |
222 // We can check this flag later if all connection options fail. | 200 // We can check this flag later if all connection options fail. |
223 certificate_expired_ = true; | 201 certificate_expired_ = true; |
224 } | 202 } |
225 | 203 |
226 buzz::AsyncSocket* SingleLoginAttempt::CreateSocket( | 204 buzz::AsyncSocket* SingleLoginAttempt::CreateSocket( |
227 const buzz::XmppClientSettings& xcs) { | 205 const buzz::XmppClientSettings& xcs) { |
228 if (use_chrome_async_socket_) { | 206 bool use_fake_ssl_client_socket = |
229 bool use_fake_ssl_client_socket = | 207 (xcs.protocol() == cricket::PROTO_SSLTCP); |
230 (xcs.protocol() == cricket::PROTO_SSLTCP); | 208 net::ClientSocketFactory* const client_socket_factory = |
231 net::ClientSocketFactory* const client_socket_factory = | 209 new XmppClientSocketFactory( |
232 new XmppClientSocketFactory( | 210 net::ClientSocketFactory::GetDefaultFactory(), |
233 net::ClientSocketFactory::GetDefaultFactory(), | 211 use_fake_ssl_client_socket); |
234 use_fake_ssl_client_socket); | 212 // The default SSLConfig is good enough for us for now. |
235 // The default SSLConfig is good enough for us for now. | 213 const net::SSLConfig ssl_config; |
236 const net::SSLConfig ssl_config; | 214 // A read buffer of 64k ought to be sufficient. |
237 // A read buffer of 64k ought to be sufficient. | 215 const size_t kReadBufSize = 64U * 1024U; |
238 const size_t kReadBufSize = 64U * 1024U; | 216 // This number was taken from a similar number in |
239 // This number was taken from a similar number in | 217 // XmppSocketAdapter. |
240 // XmppSocketAdapter. | 218 const size_t kWriteBufSize = 64U * 1024U; |
241 const size_t kWriteBufSize = 64U * 1024U; | 219 // TODO(akalin): Use a real NetLog. |
242 // TODO(akalin): Use a real NetLog. | 220 net::NetLog* const net_log = NULL; |
243 net::NetLog* const net_log = NULL; | 221 return new ChromeAsyncSocket( |
244 return new ChromeAsyncSocket( | 222 client_socket_factory, ssl_config, |
245 client_socket_factory, ssl_config, | 223 kReadBufSize, kWriteBufSize, net_log); |
246 kReadBufSize, kWriteBufSize, net_log); | |
247 } | |
248 // TODO(akalin): Always use ChromeAsyncSocket and get rid of this | |
249 // code. | |
250 bool allow_unverified_certs = | |
251 login_settings_->connection_options().allow_unverified_certs(); | |
252 XmppSocketAdapter* adapter = new XmppSocketAdapter(xcs, | |
253 allow_unverified_certs); | |
254 adapter->SignalAuthenticationError.connect( | |
255 this, | |
256 &SingleLoginAttempt::OnAuthenticationError); | |
257 if (login_settings_->firewall()) { | |
258 adapter->set_firewall(true); | |
259 } | |
260 return adapter; | |
261 } | 224 } |
262 | 225 |
263 buzz::PreXmppAuth* SingleLoginAttempt::CreatePreXmppAuth( | 226 buzz::PreXmppAuth* SingleLoginAttempt::CreatePreXmppAuth( |
264 const buzz::XmppClientSettings& xcs) { | 227 const buzz::XmppClientSettings& xcs) { |
265 buzz::Jid jid(xcs.user(), xcs.host(), buzz::STR_EMPTY); | 228 buzz::Jid jid(xcs.user(), xcs.host(), buzz::STR_EMPTY); |
266 return new GaiaTokenPreXmppAuth( | 229 return new GaiaTokenPreXmppAuth( |
267 jid.Str(), xcs.auth_cookie(), xcs.token_service()); | 230 jid.Str(), xcs.auth_cookie(), xcs.token_service()); |
268 } | 231 } |
269 | 232 |
270 void SingleLoginAttempt::OnFreshAuthCookie(const std::string& auth_cookie) { | |
271 // Remember this is a fresh cookie. | |
272 cookie_refreshed_ = true; | |
273 | |
274 // TODO(sync): do the cookie logic (part of which is in the #if 0 below). | |
275 | |
276 // The following code is what PhoneWindow does for the equivalent method. | |
277 #if 0 | |
278 // Save cookie | |
279 AccountInfo current(account_history_.current()); | |
280 current.set_auth_cookie(auth_cookie); | |
281 account_history_.set_current(current); | |
282 | |
283 // Calc next time to refresh cookie, between 5 and 10 days. The cookie has | |
284 // 14 days of life; this gives at least 4 days of retries before the current | |
285 // cookie expires, maximizing the chance of having a valid cookie next time | |
286 // the connection servers go down. | |
287 FTULL now; | |
288 | |
289 // NOTE: The following line is win32. Address this when implementing this | |
290 // code (doing "the cookie logic"). | |
291 GetSystemTimeAsFileTime(&(now.ft)); | |
292 ULONGLONG five_days = (ULONGLONG)10000 * 1000 * 60 * 60 * 24 * 5; // 5 days | |
293 ULONGLONG random = (ULONGLONG)10000 * // get to 100 ns units | |
294 ((rand() % (5 * 24 * 60)) * (60 * 1000) + // random min. in 5 day period | |
295 (rand() % 1000) * 60); // random 1/1000th of a minute | |
296 next_cookie_refresh_ = now.ull + five_days + random; // 5-10 days | |
297 #endif | |
298 } | |
299 | |
300 void SingleLoginAttempt::DiagnoseConnectionError() { | |
301 switch (code_) { | |
302 case buzz::XmppEngine::ERROR_MISSING_USERNAME: | |
303 case buzz::XmppEngine::ERROR_NETWORK_TIMEOUT: | |
304 case buzz::XmppEngine::ERROR_DOCUMENT_CLOSED: | |
305 case buzz::XmppEngine::ERROR_BIND: | |
306 case buzz::XmppEngine::ERROR_AUTH: | |
307 case buzz::XmppEngine::ERROR_TLS: | |
308 case buzz::XmppEngine::ERROR_UNAUTHORIZED: | |
309 case buzz::XmppEngine::ERROR_VERSION: | |
310 case buzz::XmppEngine::ERROR_STREAM: | |
311 case buzz::XmppEngine::ERROR_XML: | |
312 case buzz::XmppEngine::ERROR_NONE: | |
313 default: { | |
314 LoginFailure failure(LoginFailure::XMPP_ERROR, code_, subcode_); | |
315 SignalLoginFailure(failure); | |
316 return; | |
317 } | |
318 | |
319 // The following errors require diagnosistics: | |
320 // * spurious close of connection | |
321 // * socket errors after auth | |
322 case buzz::XmppEngine::ERROR_CONNECTION_CLOSED: | |
323 case buzz::XmppEngine::ERROR_SOCKET: | |
324 break; | |
325 } | |
326 | |
327 talk_base::AsyncHttpRequest *http_request = | |
328 new talk_base::AsyncHttpRequest(GetUserAgentString()); | |
329 http_request->set_host("www.google.com"); | |
330 http_request->set_port(80); | |
331 http_request->set_secure(false); | |
332 http_request->request().path = "/"; | |
333 http_request->request().verb = talk_base::HV_GET; | |
334 | |
335 talk_base::ProxyInfo proxy; | |
336 DCHECK(connection_generator_.get()); | |
337 if (connection_generator_.get()) { | |
338 proxy = connection_generator_->proxy(); | |
339 } | |
340 http_request->set_proxy(proxy); | |
341 http_request->set_firewall(login_settings_->firewall()); | |
342 | |
343 http_request->SignalWorkDone.connect(this, | |
344 &SingleLoginAttempt::OnHttpTestDone); | |
345 http_request->Start(); | |
346 http_request->Release(); | |
347 } | |
348 | |
349 void SingleLoginAttempt::OnHttpTestDone(talk_base::SignalThread* thread) { | |
350 DCHECK(thread); | |
351 | |
352 talk_base::AsyncHttpRequest* request = | |
353 static_cast<talk_base::AsyncHttpRequest*>(thread); | |
354 | |
355 if (request->response().scode == 200) { | |
356 // We were able to do an HTTP GET of www.google.com:80 | |
357 | |
358 // | |
359 // The original error should be reported | |
360 // | |
361 LoginFailure failure(LoginFailure::XMPP_ERROR, code_, subcode_); | |
362 SignalLoginFailure(failure); | |
363 return; | |
364 } | |
365 | |
366 // Otherwise lets transmute the error into ERROR_SOCKET, and put the subcode | |
367 // as an indicator of what we think the problem might be. | |
368 | |
369 #if 0 | |
370 // TODO(sync): determine if notifier has an analogous situation. | |
371 | |
372 // | |
373 // We weren't able to do an HTTP GET of www.google.com:80 | |
374 // | |
375 GAutoupdater::Version version_logged_in(g_options.version_logged_in()); | |
376 GAutoupdater::Version version_installed(GetProductVersion().c_str()); | |
377 if (version_logged_in < version_installed) { | |
378 // | |
379 // Google Talk has been updated and can no longer connect to the Google | |
380 // Talk Service. Your firewall is probably not allowing the new version of | |
381 // Google Talk to connect to the internet. Please adjust your firewall | |
382 // settings to allow the new version of Google Talk to connect to the | |
383 // internet. | |
384 // | |
385 // We'll use the "error=1" to help figure this out for now. | |
386 // | |
387 LoginFailure failure(LoginFailure::XMPP_ERROR, | |
388 buzz::XmppEngine::ERROR_SOCKET, | |
389 1); | |
390 SignalLoginFailure(failure); | |
391 return; | |
392 } | |
393 #endif | |
394 | |
395 // | |
396 // Any other checking we can add here? | |
397 // | |
398 | |
399 // | |
400 // Google Talk is unable to use your internet connection. Either your network | |
401 // isn't configured or Google Talk is being blocked by a local firewall. | |
402 // | |
403 // We'll use the "error=0" to help figure this out for now | |
404 // | |
405 LoginFailure failure(LoginFailure::XMPP_ERROR, | |
406 buzz::XmppEngine::ERROR_SOCKET, | |
407 0); | |
408 SignalLoginFailure(failure); | |
409 } | |
410 | |
411 void SingleLoginAttempt::OnClientStateChange(buzz::XmppEngine::State state) { | 233 void SingleLoginAttempt::OnClientStateChange(buzz::XmppEngine::State state) { |
412 if (state_ == state) | 234 if (state_ == state) |
413 return; | 235 return; |
414 | 236 |
415 buzz::XmppEngine::State previous_state = state_; | 237 buzz::XmppEngine::State previous_state = state_; |
416 state_ = state; | 238 state_ = state; |
417 | 239 |
418 switch (state) { | 240 switch (state) { |
419 case buzz::XmppEngine::STATE_NONE: | 241 case buzz::XmppEngine::STATE_NONE: |
420 case buzz::XmppEngine::STATE_START: | 242 case buzz::XmppEngine::STATE_START: |
421 case buzz::XmppEngine::STATE_OPENING: | 243 case buzz::XmppEngine::STATE_OPENING: |
| 244 case buzz::XmppEngine::STATE_OPEN: |
422 // Do nothing. | 245 // Do nothing. |
423 break; | 246 break; |
424 case buzz::XmppEngine::STATE_OPEN: | |
425 successful_connection_ = true; | |
426 break; | |
427 case buzz::XmppEngine::STATE_CLOSED: | 247 case buzz::XmppEngine::STATE_CLOSED: |
428 OnClientStateChangeClosed(previous_state); | 248 OnClientStateChangeClosed(previous_state); |
429 break; | 249 break; |
430 } | 250 } |
431 SignalClientStateChange(state); | 251 SignalClientStateChange(state); |
432 if (state_ == buzz::XmppEngine::STATE_CLOSED) { | 252 if (state_ == buzz::XmppEngine::STATE_CLOSED) { |
433 OnClientStateChange(buzz::XmppEngine::STATE_NONE); | 253 OnClientStateChange(buzz::XmppEngine::STATE_NONE); |
434 } | 254 } |
435 } | 255 } |
436 | 256 |
(...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
492 // There was a problem with credentials (username/password). | 312 // There was a problem with credentials (username/password). |
493 HandleConnectionPasswordError(); | 313 HandleConnectionPasswordError(); |
494 return; | 314 return; |
495 } | 315 } |
496 | 316 |
497 // Unexpected disconnect, | 317 // Unexpected disconnect, |
498 // Unreachable host, | 318 // Unreachable host, |
499 // Or internal server binding error - | 319 // Or internal server binding error - |
500 // All these are temporary problems, so continue reconnecting. | 320 // All these are temporary problems, so continue reconnecting. |
501 | 321 |
502 // GaiaAuth signals this directly via SignalCertificateExpired, but | |
503 // SChannelAdapter propagates the error through SocketWindow as a socket | |
504 // error. | |
505 if (code_ == buzz::XmppEngine::ERROR_SOCKET && | |
506 subcode_ == SEC_E_CERT_EXPIRED) { | |
507 certificate_expired_ = true; | |
508 } | |
509 | |
510 login_settings_->modifiable_user_settings()->set_resource(""); | 322 login_settings_->modifiable_user_settings()->set_resource(""); |
511 | 323 |
512 // Look for stream::error server redirection stanza "see-other-host". | 324 // Look for stream::error server redirection stanza "see-other-host". |
513 if (stream_error) { | 325 if (stream_error) { |
514 const buzz::XmlElement* other = | 326 const buzz::XmlElement* other = |
515 stream_error->FirstNamed(buzz::QN_XSTREAM_SEE_OTHER_HOST); | 327 stream_error->FirstNamed(buzz::QN_XSTREAM_SEE_OTHER_HOST); |
516 if (other) { | 328 if (other) { |
517 const buzz::XmlElement* text = | 329 const buzz::XmlElement* text = |
518 stream_error->FirstNamed(buzz::QN_XSTREAM_TEXT); | 330 stream_error->FirstNamed(buzz::QN_XSTREAM_TEXT); |
519 if (text) { | 331 if (text) { |
(...skipping 25 matching lines...) Expand all Loading... |
545 DCHECK(connection_generator_.get()); | 357 DCHECK(connection_generator_.get()); |
546 if (!connection_generator_.get()) { | 358 if (!connection_generator_.get()) { |
547 return; | 359 return; |
548 } | 360 } |
549 | 361 |
550 // Iterate to the next possible connection (still trying to connect). | 362 // Iterate to the next possible connection (still trying to connect). |
551 UseNextConnection(); | 363 UseNextConnection(); |
552 } | 364 } |
553 | 365 |
554 } // namespace notifier | 366 } // namespace notifier |
OLD | NEW |