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