1 /* 2 * libjingle 3 * Copyright 2004--2005, Google Inc. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are met: 7 * 8 * 1. Redistributions of source code must retain the above copyright notice, 9 * this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright notice, 11 * this list of conditions and the following disclaimer in the documentation 12 * and/or other materials provided with the distribution. 13 * 3. The name of the author may not be used to endorse or promote products 14 * derived from this software without specific prior written permission. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 17 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 18 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO 19 * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 20 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 22 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 23 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 24 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 25 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 */ 27 28 #include "xmppclient.h" 29 #include "xmpptask.h" 30 #include "talk/base/logging.h" 31 #include "talk/base/sigslot.h" 32 #include "talk/base/scoped_ptr.h" 33 #include "talk/base/stringutils.h" 34 #include "talk/xmpp/constants.h" 35 #include "talk/xmpp/saslplainmechanism.h" 36 #include "talk/xmpp/prexmppauth.h" 37 #include "talk/xmpp/plainsaslhandler.h" 38 39 namespace buzz { 40 41 class XmppClient::Private : 42 public sigslot::has_slots<>, 43 public XmppSessionHandler, 44 public XmppOutputHandler { 45 public: 46 47 explicit Private(XmppClient* client) : 48 client_(client), 49 socket_(), 50 engine_(), 51 proxy_port_(0), 52 pre_engine_error_(XmppEngine::ERROR_NONE), 53 pre_engine_subcode_(0), 54 signal_closed_(false), 55 allow_plain_(false) {} 56 57 virtual ~Private() { 58 // We need to disconnect from socket_ before engine_ is destructed (by 59 // the auto-generated destructor code). 60 ResetSocket(); 61 } 62 63 // the owner 64 XmppClient* const client_; 65 66 // the two main objects 67 talk_base::scoped_ptr<AsyncSocket> socket_; 68 talk_base::scoped_ptr<XmppEngine> engine_; 69 talk_base::scoped_ptr<PreXmppAuth> pre_auth_; 70 talk_base::CryptString pass_; 71 std::string auth_mechanism_; 72 std::string auth_token_; 73 talk_base::SocketAddress server_; 74 std::string proxy_host_; 75 int proxy_port_; 76 XmppEngine::Error pre_engine_error_; 77 int pre_engine_subcode_; 78 CaptchaChallenge captcha_challenge_; 79 bool signal_closed_; 80 bool allow_plain_; 81 82 void ResetSocket() { 83 if (socket_) { 84 socket_->SignalConnected.disconnect(this); 85 socket_->SignalRead.disconnect(this); 86 socket_->SignalClosed.disconnect(this); 87 socket_.reset(NULL); 88 } 89 } 90 91 // implementations of interfaces 92 void OnStateChange(int state); 93 void WriteOutput(const char* bytes, size_t len); 94 void StartTls(const std::string& domainname); 95 void CloseConnection(); 96 97 // slots for socket signals 98 void OnSocketConnected(); 99 void OnSocketRead(); 100 void OnSocketClosed(); 101 }; 102 103 bool IsTestServer(const std::string& server_name, 104 const std::string& test_server_domain) { 105 return (!test_server_domain.empty() && 106 talk_base::ends_with(server_name.c_str(), 107 test_server_domain.c_str())); 108 } 109 110 XmppReturnStatus XmppClient::Connect( 111 const XmppClientSettings& settings, 112 const std::string& lang, AsyncSocket* socket, PreXmppAuth* pre_auth) { 113 if (socket == NULL) 114 return XMPP_RETURN_BADARGUMENT; 115 if (d_->socket_) 116 return XMPP_RETURN_BADSTATE; 117 118 d_->socket_.reset(socket); 119 120 d_->socket_->SignalConnected.connect(d_.get(), &Private::OnSocketConnected); 121 d_->socket_->SignalRead.connect(d_.get(), &Private::OnSocketRead); 122 d_->socket_->SignalClosed.connect(d_.get(), &Private::OnSocketClosed); 123 124 d_->engine_.reset(XmppEngine::Create()); 125 d_->engine_->SetSessionHandler(d_.get()); 126 d_->engine_->SetOutputHandler(d_.get()); 127 if (!settings.resource().empty()) { 128 d_->engine_->SetRequestedResource(settings.resource()); 129 } 130 d_->engine_->SetTls(settings.use_tls()); 131 132 // The talk.google.com server returns a certificate with common-name: 133 // CN="gmail.com" for @gmail.com accounts, 134 // CN="googlemail.com" for @googlemail.com accounts, 135 // CN="talk.google.com" for other accounts (such as @example.com), 136 // so we tweak the tls server setting for those other accounts to match the 137 // returned certificate CN of "talk.google.com". 138 // For other servers, we leave the strings empty, which causes the jid's 139 // domain to be used. We do the same for gmail.com and googlemail.com as the 140 // returned CN matches the account domain in those cases. 141 std::string server_name = settings.server().HostAsURIString(); 142 if (server_name == buzz::STR_TALK_GOOGLE_COM || 143 server_name == buzz::STR_TALKX_L_GOOGLE_COM || 144 server_name == buzz::STR_XMPP_GOOGLE_COM || 145 server_name == buzz::STR_XMPPX_L_GOOGLE_COM || 146 IsTestServer(server_name, settings.test_server_domain())) { 147 if (settings.host() != STR_GMAIL_COM && 148 settings.host() != STR_GOOGLEMAIL_COM) { 149 d_->engine_->SetTlsServer("", STR_TALK_GOOGLE_COM); 150 } 151 } 152 153 // Set language 154 d_->engine_->SetLanguage(lang); 155 156 d_->engine_->SetUser(buzz::Jid(settings.user(), settings.host(), STR_EMPTY)); 157 158 d_->pass_ = settings.pass(); 159 d_->auth_mechanism_ = settings.auth_mechanism(); 160 d_->auth_token_ = settings.auth_token(); 161 d_->server_ = settings.server(); 162 d_->proxy_host_ = settings.proxy_host(); 163 d_->proxy_port_ = settings.proxy_port(); 164 d_->allow_plain_ = settings.allow_plain(); 165 d_->pre_auth_.reset(pre_auth); 166 167 return XMPP_RETURN_OK; 168 } 169 170 XmppEngine::State XmppClient::GetState() const { 171 if (!d_->engine_) 172 return XmppEngine::STATE_NONE; 173 return d_->engine_->GetState(); 174 } 175 176 XmppEngine::Error XmppClient::GetError(int* subcode) { 177 if (subcode) { 178 *subcode = 0; 179 } 180 if (!d_->engine_) 181 return XmppEngine::ERROR_NONE; 182 if (d_->pre_engine_error_ != XmppEngine::ERROR_NONE) { 183 if (subcode) { 184 *subcode = d_->pre_engine_subcode_; 185 } 186 return d_->pre_engine_error_; 187 } 188 return d_->engine_->GetError(subcode); 189 } 190 191 const XmlElement* XmppClient::GetStreamError() { 192 if (!d_->engine_) { 193 return NULL; 194 } 195 return d_->engine_->GetStreamError(); 196 } 197 198 CaptchaChallenge XmppClient::GetCaptchaChallenge() { 199 if (!d_->engine_) 200 return CaptchaChallenge(); 201 return d_->captcha_challenge_; 202 } 203 204 std::string XmppClient::GetAuthMechanism() { 205 if (!d_->engine_) 206 return ""; 207 return d_->auth_mechanism_; 208 } 209 210 std::string XmppClient::GetAuthToken() { 211 if (!d_->engine_) 212 return ""; 213 return d_->auth_token_; 214 } 215 216 int XmppClient::ProcessStart() { 217 // Should not happen, but was observed in crash reports 218 if (!d_->socket_) { 219 LOG(LS_ERROR) << "socket_ already reset"; 220 return STATE_DONE; 221 } 222 223 if (d_->pre_auth_) { 224 d_->pre_auth_->SignalAuthDone.connect(this, &XmppClient::OnAuthDone); 225 d_->pre_auth_->StartPreXmppAuth( 226 d_->engine_->GetUser(), d_->server_, d_->pass_, 227 d_->auth_mechanism_, d_->auth_token_); 228 d_->pass_.Clear(); // done with this; 229 return STATE_PRE_XMPP_LOGIN; 230 } 231 else { 232 d_->engine_->SetSaslHandler(new PlainSaslHandler( 233 d_->engine_->GetUser(), d_->pass_, d_->allow_plain_)); 234 d_->pass_.Clear(); // done with this; 235 return STATE_START_XMPP_LOGIN; 236 } 237 } 238 239 void XmppClient::OnAuthDone() { 240 Wake(); 241 } 242 243 int XmppClient::ProcessTokenLogin() { 244 // Should not happen, but was observed in crash reports 245 if (!d_->socket_) { 246 LOG(LS_ERROR) << "socket_ already reset"; 247 return STATE_DONE; 248 } 249 250 // Don't know how this could happen, but crash reports show it as NULL 251 if (!d_->pre_auth_) { 252 d_->pre_engine_error_ = XmppEngine::ERROR_AUTH; 253 EnsureClosed(); 254 return STATE_ERROR; 255 } 256 257 // Wait until pre authentication is done is done 258 if (!d_->pre_auth_->IsAuthDone()) 259 return STATE_BLOCKED; 260 261 if (!d_->pre_auth_->IsAuthorized()) { 262 // maybe split out a case when gaia is down? 263 if (d_->pre_auth_->HadError()) { 264 d_->pre_engine_error_ = XmppEngine::ERROR_AUTH; 265 d_->pre_engine_subcode_ = d_->pre_auth_->GetError(); 266 } 267 else { 268 d_->pre_engine_error_ = XmppEngine::ERROR_UNAUTHORIZED; 269 d_->pre_engine_subcode_ = 0; 270 d_->captcha_challenge_ = d_->pre_auth_->GetCaptchaChallenge(); 271 } 272 d_->pre_auth_.reset(NULL); // done with this 273 EnsureClosed(); 274 return STATE_ERROR; 275 } 276 277 // Save auth token as a result 278 279 d_->auth_mechanism_ = d_->pre_auth_->GetAuthMechanism(); 280 d_->auth_token_ = d_->pre_auth_->GetAuthToken(); 281 282 // transfer ownership of pre_auth_ to engine 283 d_->engine_->SetSaslHandler(d_->pre_auth_.release()); 284 return STATE_START_XMPP_LOGIN; 285 } 286 287 int XmppClient::ProcessStartXmppLogin() { 288 // Should not happen, but was observed in crash reports 289 if (!d_->socket_) { 290 LOG(LS_ERROR) << "socket_ already reset"; 291 return STATE_DONE; 292 } 293 294 // Done with pre-connect tasks - connect! 295 if (!d_->socket_->Connect(d_->server_)) { 296 EnsureClosed(); 297 return STATE_ERROR; 298 } 299 300 return STATE_RESPONSE; 301 } 302 303 int XmppClient::ProcessResponse() { 304 // Hang around while we are connected. 305 if (!delivering_signal_ && 306 (!d_->engine_ || d_->engine_->GetState() == XmppEngine::STATE_CLOSED)) 307 return STATE_DONE; 308 return STATE_BLOCKED; 309 } 310 311 XmppReturnStatus XmppClient::Disconnect() { 312 if (!d_->socket_) 313 return XMPP_RETURN_BADSTATE; 314 Abort(); 315 d_->engine_->Disconnect(); 316 d_->ResetSocket(); 317 return XMPP_RETURN_OK; 318 } 319 320 XmppClient::XmppClient(TaskParent* parent) 321 : XmppTaskParentInterface(parent), 322 delivering_signal_(false), 323 valid_(false) { 324 d_.reset(new Private(this)); 325 valid_ = true; 326 } 327 328 XmppClient::~XmppClient() { 329 valid_ = false; 330 } 331 332 const Jid& XmppClient::jid() const { 333 return d_->engine_->FullJid(); 334 } 335 336 337 std::string XmppClient::NextId() { 338 return d_->engine_->NextId(); 339 } 340 341 XmppReturnStatus XmppClient::SendStanza(const XmlElement* stanza) { 342 return d_->engine_->SendStanza(stanza); 343 } 344 345 XmppReturnStatus XmppClient::SendStanzaError( 346 const XmlElement* old_stanza, XmppStanzaError xse, 347 const std::string& message) { 348 return d_->engine_->SendStanzaError(old_stanza, xse, message); 349 } 350 351 XmppReturnStatus XmppClient::SendRaw(const std::string& text) { 352 return d_->engine_->SendRaw(text); 353 } 354 355 XmppEngine* XmppClient::engine() { 356 return d_->engine_.get(); 357 } 358 359 void XmppClient::Private::OnSocketConnected() { 360 engine_->Connect(); 361 } 362 363 void XmppClient::Private::OnSocketRead() { 364 char bytes[4096]; 365 size_t bytes_read; 366 for (;;) { 367 // Should not happen, but was observed in crash reports 368 if (!socket_) { 369 LOG(LS_ERROR) << "socket_ already reset"; 370 return; 371 } 372 373 if (!socket_->Read(bytes, sizeof(bytes), &bytes_read)) { 374 // TODO: deal with error information 375 return; 376 } 377 378 if (bytes_read == 0) 379 return; 380 381 //#ifdef _DEBUG 382 client_->SignalLogInput(bytes, static_cast<int>(bytes_read)); 383 //#endif 384 385 engine_->HandleInput(bytes, bytes_read); 386 } 387 } 388 389 void XmppClient::Private::OnSocketClosed() { 390 int code = socket_->GetError(); 391 engine_->ConnectionClosed(code); 392 } 393 394 void XmppClient::Private::OnStateChange(int state) { 395 if (state == XmppEngine::STATE_CLOSED) { 396 client_->EnsureClosed(); 397 } 398 else { 399 client_->SignalStateChange((XmppEngine::State)state); 400 } 401 client_->Wake(); 402 } 403 404 void XmppClient::Private::WriteOutput(const char* bytes, size_t len) { 405 //#ifdef _DEBUG 406 client_->SignalLogOutput(bytes, static_cast<int>(len)); 407 //#endif 408 409 socket_->Write(bytes, len); 410 // TODO: deal with error information 411 } 412 413 void XmppClient::Private::StartTls(const std::string& domain) { 414 #if defined(FEATURE_ENABLE_SSL) 415 socket_->StartTls(domain); 416 #endif 417 } 418 419 void XmppClient::Private::CloseConnection() { 420 socket_->Close(); 421 } 422 423 void XmppClient::AddXmppTask(XmppTask* task, XmppEngine::HandlerLevel level) { 424 d_->engine_->AddStanzaHandler(task, level); 425 } 426 427 void XmppClient::RemoveXmppTask(XmppTask* task) { 428 d_->engine_->RemoveStanzaHandler(task); 429 } 430 431 void XmppClient::EnsureClosed() { 432 if (!d_->signal_closed_) { 433 d_->signal_closed_ = true; 434 delivering_signal_ = true; 435 SignalStateChange(XmppEngine::STATE_CLOSED); 436 delivering_signal_ = false; 437 } 438 } 439 440 } // namespace buzz 441