15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright (c) 2012 The Chromium Authors. All rights reserved. 25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be 35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file. 45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <string> 65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "jingle/notifier/communicator/single_login_attempt.h" 85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/basictypes.h" 105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/logging.h" 11868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#include "base/strings/string_number_conversions.h" 122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/strings/string_split.h" 135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "jingle/notifier/base/const_communicator.h" 145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "jingle/notifier/base/gaia_token_pre_xmpp_auth.h" 155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "jingle/notifier/listener/xml_element_util.h" 165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "net/base/host_port_pair.h" 175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "talk/xmllite/xmlelement.h" 185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "talk/xmpp/constants.h" 195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "talk/xmpp/xmppclientsettings.h" 205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace notifier { 225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)SingleLoginAttempt::Delegate::~Delegate() {} 245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)SingleLoginAttempt::SingleLoginAttempt(const LoginSettings& login_settings, 265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) Delegate* delegate) 275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) : login_settings_(login_settings), 285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) delegate_(delegate), 295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) settings_list_( 305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) MakeConnectionSettingsList(login_settings_.GetServers(), 315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) login_settings_.try_ssltcp_first())), 325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) current_settings_(settings_list_.begin()) { 335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (settings_list_.empty()) { 345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) NOTREACHED(); 355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return; 365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) TryConnect(*current_settings_); 385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)SingleLoginAttempt::~SingleLoginAttempt() {} 415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// In the code below, we assume that calling a delegate method may end 435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// up in ourselves being deleted, so we always call it last. 445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// 455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// TODO(akalin): Add unit tests to enforce the behavior above. 465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void SingleLoginAttempt::OnConnect( 485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) base::WeakPtr<buzz::XmppTaskParentInterface> base_task) { 495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DVLOG(1) << "Connected to " << current_settings_->ToString(); 505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) delegate_->OnConnect(base_task); 515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace { 545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// This function is more permissive than 565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// net::HostPortPair::FromString(). If the port is missing or 575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// unparseable, it assumes the default XMPP port. The hostname may be 585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// empty. 595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)net::HostPortPair ParseRedirectText(const std::string& redirect_text) { 605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) std::vector<std::string> parts; 615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) base::SplitString(redirect_text, ':', &parts); 625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) net::HostPortPair redirect_server; 635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) redirect_server.set_port(kDefaultXmppPort); 645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (parts.empty()) { 655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return redirect_server; 665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) redirect_server.set_host(parts[0]); 685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (parts.size() <= 1) { 695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return redirect_server; 705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Try to parse the port, falling back to kDefaultXmppPort. 725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) int port = kDefaultXmppPort; 735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!base::StringToInt(parts[1], &port)) { 745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) port = kDefaultXmppPort; 755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (port <= 0 || port > kuint16max) { 775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) port = kDefaultXmppPort; 785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) redirect_server.set_port(port); 805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return redirect_server; 815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} // namespace 845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void SingleLoginAttempt::OnError(buzz::XmppEngine::Error error, int subcode, 865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) const buzz::XmlElement* stream_error) { 875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DVLOG(1) << "Error: " << error << ", subcode: " << subcode 88c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) << (stream_error 89c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) ? (", stream error: " + XmlElementToString(*stream_error)) 90c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) : std::string()); 915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DCHECK_EQ(error == buzz::XmppEngine::ERROR_STREAM, stream_error != NULL); 935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Check for redirection. We expect something like: 955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // 965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // <stream:error><see-other-host xmlns="urn:ietf:params:xml:ns:xmpp-streams"/><str:text xmlns:str="urn:ietf:params:xml:ns:xmpp-streams">talk.google.com</str:text></stream:error> [2] 975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // 985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // There are some differences from the spec [1]: 995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // 1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // - we expect a separate text element with the redirection info 1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // (which is the format Google Talk's servers use), whereas the 1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // spec puts the redirection info directly in the see-other-host 1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // element; 1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // - we check for redirection only during login, whereas the 1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // server can send down a redirection at any time according to 1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // the spec. (TODO(akalin): Figure out whether we need to handle 1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // redirection at any other point.) 1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // 1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // [1]: http://xmpp.org/internet-drafts/draft-saintandre-rfc3920bis-08.html#streams-error-conditions-see-other-host 1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // [2]: http://forums.miranda-im.org/showthread.php?24376-GoogleTalk-drops 1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (stream_error) { 1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) const buzz::XmlElement* other = 1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) stream_error->FirstNamed(buzz::QN_XSTREAM_SEE_OTHER_HOST); 1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (other) { 1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) const buzz::XmlElement* text = 1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) stream_error->FirstNamed(buzz::QN_XSTREAM_TEXT); 1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (text) { 1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Yep, its a "stream:error" with "see-other-host" text, 1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // let's parse out the server:port, and then reconnect 1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // with that. 1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) const net::HostPortPair& redirect_server = 1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ParseRedirectText(text->BodyText()); 1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // ParseRedirectText shouldn't return a zero port. 1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DCHECK_NE(redirect_server.port(), 0u); 1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // If we don't have a host, ignore the redirection and treat 1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // it like a regular error. 1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!redirect_server.host().empty()) { 1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) delegate_->OnRedirect( 1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ServerInformation( 1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) redirect_server, 1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) current_settings_->ssltcp_support)); 1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // May be deleted at this point. 1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return; 1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (error == buzz::XmppEngine::ERROR_UNAUTHORIZED) { 1402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) DVLOG(1) << "Credentials rejected"; 1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) delegate_->OnCredentialsRejected(); 1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return; 1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (current_settings_ == settings_list_.end()) { 1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) NOTREACHED(); 1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return; 1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ++current_settings_; 1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (current_settings_ == settings_list_.end()) { 1522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) DVLOG(1) << "Could not connect to any XMPP server"; 1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) delegate_->OnSettingsExhausted(); 1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return; 1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) TryConnect(*current_settings_); 1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void SingleLoginAttempt::TryConnect( 1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) const ConnectionSettings& connection_settings) { 1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DVLOG(1) << "Trying to connect to " << connection_settings.ToString(); 1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Copy the user settings and fill in the connection parameters from 1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // |connection_settings|. 1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) buzz::XmppClientSettings client_settings = login_settings_.user_settings(); 1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) connection_settings.FillXmppClientSettings(&client_settings); 1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) buzz::Jid jid(client_settings.user(), client_settings.host(), 1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) buzz::STR_EMPTY); 1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) buzz::PreXmppAuth* pre_xmpp_auth = 1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) new GaiaTokenPreXmppAuth( 1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) jid.Str(), client_settings.auth_token(), 1735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) client_settings.token_service(), 1745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) login_settings_.auth_mechanism()); 1755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) xmpp_connection_.reset( 1765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) new XmppConnection(client_settings, 1775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) login_settings_.request_context_getter(), 1785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this, 1795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) pre_xmpp_auth)); 1805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 1815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} // namespace notifier 183