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 #define TRACK_ARRAY_ALLOC_PROBLEM 29 30 #include <vector> 31 #include <sstream> 32 #include <algorithm> 33 #include "talk/xmllite/xmlelement.h" 34 #include "talk/base/common.h" 35 #include "talk/xmpp/xmppengineimpl.h" 36 #include "talk/xmpp/xmpplogintask.h" 37 #include "talk/xmpp/constants.h" 38 #include "talk/xmllite/xmlprinter.h" 39 #include "talk/xmpp/saslhandler.h" 40 41 namespace buzz { 42 43 static const std::string XMPP_CLIENT_NAMESPACES[] = { 44 "stream", "http://etherx.jabber.org/streams", 45 "", "jabber:client", 46 }; 47 48 static const size_t XMPP_CLIENT_NAMESPACES_LEN = 4; 49 50 XmppEngine * XmppEngine::Create() { 51 return new XmppEngineImpl(); 52 } 53 54 55 XmppEngineImpl::XmppEngineImpl() : 56 stanzaParseHandler_(this), 57 stanzaParser_(&stanzaParseHandler_), 58 engine_entered_(0), 59 user_jid_(JID_EMPTY), 60 password_(), 61 requested_resource_(STR_EMPTY), 62 tls_needed_(true), 63 login_task_(new XmppLoginTask(this)), 64 next_id_(0), 65 bound_jid_(JID_EMPTY), 66 state_(STATE_START), 67 encrypted_(false), 68 error_code_(ERROR_NONE), 69 subcode_(0), 70 stream_error_(NULL), 71 raised_reset_(false), 72 output_handler_(NULL), 73 session_handler_(NULL), 74 iq_entries_(new IqEntryVector()), 75 sasl_handler_(NULL), 76 output_(new std::stringstream()) { 77 for (int i = 0; i < HL_COUNT; i+= 1) { 78 stanza_handlers_[i].reset(new StanzaHandlerVector()); 79 } 80 } 81 82 XmppEngineImpl::~XmppEngineImpl() { 83 DeleteIqCookies(); 84 } 85 86 XmppReturnStatus 87 XmppEngineImpl::SetOutputHandler(XmppOutputHandler* output_handler) { 88 if (state_ != STATE_START) 89 return XMPP_RETURN_BADSTATE; 90 91 output_handler_ = output_handler; 92 93 return XMPP_RETURN_OK; 94 } 95 96 XmppReturnStatus 97 XmppEngineImpl::SetSessionHandler(XmppSessionHandler* session_handler) { 98 if (state_ != STATE_START) 99 return XMPP_RETURN_BADSTATE; 100 101 session_handler_ = session_handler; 102 103 return XMPP_RETURN_OK; 104 } 105 106 XmppReturnStatus 107 XmppEngineImpl::HandleInput(const char * bytes, size_t len) { 108 if (state_ < STATE_OPENING || state_ > STATE_OPEN) 109 return XMPP_RETURN_BADSTATE; 110 111 EnterExit ee(this); 112 113 // TODO: The return value of the xml parser is not checked. 114 stanzaParser_.Parse(bytes, len, false); 115 116 return XMPP_RETURN_OK; 117 } 118 119 XmppReturnStatus 120 XmppEngineImpl::ConnectionClosed(int subcode) { 121 if (state_ != STATE_CLOSED) { 122 EnterExit ee(this); 123 // If told that connection closed and not already closed, 124 // then connection was unpexectedly dropped. 125 if (subcode) { 126 SignalError(ERROR_SOCKET, subcode); 127 } else { 128 SignalError(ERROR_CONNECTION_CLOSED, 0); // no subcode 129 } 130 } 131 return XMPP_RETURN_OK; 132 } 133 134 XmppReturnStatus 135 XmppEngineImpl::SetUseTls(bool useTls) { 136 if (state_ != STATE_START) 137 return XMPP_RETURN_BADSTATE; 138 139 tls_needed_ = useTls; 140 141 return XMPP_RETURN_OK; 142 } 143 144 XmppReturnStatus 145 XmppEngineImpl::SetTlsServer(const std::string & tls_server_hostname, 146 const std::string & tls_server_domain) { 147 if (state_ != STATE_START) 148 return XMPP_RETURN_BADSTATE; 149 150 tls_server_hostname_ = tls_server_hostname; 151 tls_server_domain_= tls_server_domain; 152 153 return XMPP_RETURN_OK; 154 } 155 156 bool 157 XmppEngineImpl::GetUseTls() { 158 return tls_needed_; 159 } 160 161 XmppReturnStatus 162 XmppEngineImpl::SetUser(const Jid & jid) { 163 if (state_ != STATE_START) 164 return XMPP_RETURN_BADSTATE; 165 166 user_jid_ = jid; 167 168 return XMPP_RETURN_OK; 169 } 170 171 const Jid & 172 XmppEngineImpl::GetUser() { 173 return user_jid_; 174 } 175 176 XmppReturnStatus 177 XmppEngineImpl::SetSaslHandler(SaslHandler * sasl_handler) { 178 if (state_ != STATE_START) 179 return XMPP_RETURN_BADSTATE; 180 181 sasl_handler_.reset(sasl_handler); 182 return XMPP_RETURN_OK; 183 } 184 185 XmppReturnStatus 186 XmppEngineImpl::SetRequestedResource(const std::string & resource) { 187 if (state_ != STATE_START) 188 return XMPP_RETURN_BADSTATE; 189 190 requested_resource_ = resource; 191 192 return XMPP_RETURN_OK; 193 } 194 195 const std::string & 196 XmppEngineImpl::GetRequestedResource() { 197 return requested_resource_; 198 } 199 200 XmppReturnStatus 201 XmppEngineImpl::AddStanzaHandler(XmppStanzaHandler * stanza_handler, 202 XmppEngine::HandlerLevel level) { 203 if (state_ == STATE_CLOSED) 204 return XMPP_RETURN_BADSTATE; 205 206 stanza_handlers_[level]->push_back(stanza_handler); 207 208 return XMPP_RETURN_OK; 209 } 210 211 XmppReturnStatus 212 XmppEngineImpl::RemoveStanzaHandler(XmppStanzaHandler * stanza_handler) { 213 214 bool found = false; 215 216 for (int level = 0; level < HL_COUNT; level += 1) { 217 StanzaHandlerVector::iterator new_end = 218 std::remove(stanza_handlers_[level]->begin(), 219 stanza_handlers_[level]->end(), 220 stanza_handler); 221 222 if (new_end != stanza_handlers_[level]->end()) { 223 stanza_handlers_[level]->erase(new_end, stanza_handlers_[level]->end()); 224 found = true; 225 } 226 } 227 228 if (!found) { 229 return XMPP_RETURN_BADARGUMENT; 230 } 231 232 return XMPP_RETURN_OK; 233 } 234 235 XmppReturnStatus 236 XmppEngineImpl::Connect() { 237 if (state_ != STATE_START) 238 return XMPP_RETURN_BADSTATE; 239 240 EnterExit ee(this); 241 242 // get the login task started 243 state_ = STATE_OPENING; 244 if (login_task_.get()) { 245 login_task_->IncomingStanza(NULL, false); 246 if (login_task_->IsDone()) 247 login_task_.reset(); 248 } 249 250 return XMPP_RETURN_OK; 251 } 252 253 XmppReturnStatus 254 XmppEngineImpl::SendStanza(const XmlElement * element) { 255 if (state_ == STATE_CLOSED) 256 return XMPP_RETURN_BADSTATE; 257 258 EnterExit ee(this); 259 260 if (login_task_.get()) { 261 // still handshaking - then outbound stanzas are queued 262 login_task_->OutgoingStanza(element); 263 } else { 264 // handshake done - send straight through 265 InternalSendStanza(element); 266 } 267 268 return XMPP_RETURN_OK; 269 } 270 271 XmppReturnStatus 272 XmppEngineImpl::SendRaw(const std::string & text) { 273 if (state_ == STATE_CLOSED || login_task_.get()) 274 return XMPP_RETURN_BADSTATE; 275 276 EnterExit ee(this); 277 278 (*output_) << text; 279 280 return XMPP_RETURN_OK; 281 } 282 283 std::string 284 XmppEngineImpl::NextId() { 285 std::stringstream ss; 286 ss << next_id_++; 287 return ss.str(); 288 } 289 290 XmppReturnStatus 291 XmppEngineImpl::Disconnect() { 292 293 if (state_ != STATE_CLOSED) { 294 EnterExit ee(this); 295 if (state_ == STATE_OPEN) 296 *output_ << "</stream:stream>"; 297 state_ = STATE_CLOSED; 298 } 299 300 return XMPP_RETURN_OK; 301 } 302 303 void 304 XmppEngineImpl::IncomingStart(const XmlElement * pelStart) { 305 if (HasError() || raised_reset_) 306 return; 307 308 if (login_task_.get()) { 309 // start-stream should go to login task 310 login_task_->IncomingStanza(pelStart, true); 311 if (login_task_->IsDone()) 312 login_task_.reset(); 313 } 314 else { 315 // if not logging in, it's an error to see a start 316 SignalError(ERROR_XML, 0); 317 } 318 } 319 320 void 321 XmppEngineImpl::IncomingStanza(const XmlElement * stanza) { 322 if (HasError() || raised_reset_) 323 return; 324 325 if (stanza->Name() == QN_STREAM_ERROR) { 326 // Explicit XMPP stream error 327 SignalStreamError(stanza); 328 } else if (login_task_.get()) { 329 // Handle login handshake 330 login_task_->IncomingStanza(stanza, false); 331 if (login_task_->IsDone()) 332 login_task_.reset(); 333 } else if (HandleIqResponse(stanza)) { 334 // iq is handled by above call 335 } else { 336 // give every "peek" handler a shot at all stanzas 337 for (size_t i = 0; i < stanza_handlers_[HL_PEEK]->size(); i += 1) { 338 (*stanza_handlers_[HL_PEEK])[i]->HandleStanza(stanza); 339 } 340 341 // give other handlers a shot in precedence order, stopping after handled 342 for (int level = HL_SINGLE; level <= HL_ALL; level += 1) { 343 for (size_t i = 0; i < stanza_handlers_[level]->size(); i += 1) { 344 if ((*stanza_handlers_[level])[i]->HandleStanza(stanza)) 345 goto Handled; 346 } 347 } 348 349 // If nobody wants to handle a stanza then send back an error. 350 // Only do this for IQ stanzas as messages should probably just be dropped 351 // and presence stanzas should certainly be dropped. 352 std::string type = stanza->Attr(QN_TYPE); 353 if (stanza->Name() == QN_IQ && 354 !(type == "error" || type == "result")) { 355 SendStanzaError(stanza, XSE_FEATURE_NOT_IMPLEMENTED, STR_EMPTY); 356 } 357 } 358 Handled: 359 ; // handled - we're done 360 } 361 362 void 363 XmppEngineImpl::IncomingEnd(bool isError) { 364 if (HasError() || raised_reset_) 365 return; 366 367 SignalError(isError ? ERROR_XML : ERROR_DOCUMENT_CLOSED, 0); 368 } 369 370 void 371 XmppEngineImpl::InternalSendStart(const std::string & to) { 372 std::string hostname = tls_server_hostname_; 373 if (hostname.empty()) { 374 hostname = to; 375 } 376 377 // If not language is specified, the spec says use * 378 std::string lang = lang_; 379 if (lang.length() == 0) 380 lang = "*"; 381 382 // send stream-beginning 383 // note, we put a \r\n at tne end fo the first line to cause non-XMPP 384 // line-oriented servers (e.g., Apache) to reveal themselves more quickly. 385 *output_ << "<stream:stream to=\"" << hostname << "\" " 386 << "xml:lang=\"" << lang << "\" " 387 << "version=\"1.0\" " 388 << "xmlns:stream=\"http://etherx.jabber.org/streams\" " 389 << "xmlns=\"jabber:client\">\r\n"; 390 } 391 392 void 393 XmppEngineImpl::InternalSendStanza(const XmlElement * element) { 394 // It should really never be necessary to set a FROM attribute on a stanza. 395 // It is implied by the bind on the stream and if you get it wrong 396 // (by flipping from/to on a message?) the server will close the stream. 397 ASSERT(!element->HasAttr(QN_FROM)); 398 399 // TODO: consider caching the XmlPrinter 400 XmlPrinter::PrintXml(output_.get(), element, 401 XMPP_CLIENT_NAMESPACES, XMPP_CLIENT_NAMESPACES_LEN); 402 } 403 404 std::string 405 XmppEngineImpl::ChooseBestSaslMechanism(const std::vector<std::string> & mechanisms, bool encrypted) { 406 return sasl_handler_->ChooseBestSaslMechanism(mechanisms, encrypted); 407 } 408 409 SaslMechanism * 410 XmppEngineImpl::GetSaslMechanism(const std::string & name) { 411 return sasl_handler_->CreateSaslMechanism(name); 412 } 413 414 void 415 XmppEngineImpl::SignalBound(const Jid & fullJid) { 416 if (state_ == STATE_OPENING) { 417 bound_jid_ = fullJid; 418 state_ = STATE_OPEN; 419 } 420 } 421 422 void 423 XmppEngineImpl::SignalStreamError(const XmlElement * pelStreamError) { 424 if (state_ != STATE_CLOSED) { 425 stream_error_.reset(new XmlElement(*pelStreamError)); 426 SignalError(ERROR_STREAM, 0); 427 } 428 } 429 430 void 431 XmppEngineImpl::SignalError(Error errorCode, int subCode) { 432 if (state_ != STATE_CLOSED) { 433 error_code_ = errorCode; 434 subcode_ = subCode; 435 state_ = STATE_CLOSED; 436 } 437 } 438 439 bool 440 XmppEngineImpl::HasError() { 441 return error_code_ != ERROR_NONE; 442 } 443 444 void 445 XmppEngineImpl::StartTls(const std::string & domain) { 446 if (output_handler_) { 447 output_handler_->StartTls( 448 tls_server_domain_.empty() ? domain : tls_server_domain_); 449 encrypted_ = true; 450 } 451 } 452 453 XmppEngineImpl::EnterExit::EnterExit(XmppEngineImpl* engine) 454 : engine_(engine), 455 state_(engine->state_), 456 error_(engine->error_code_) { 457 engine->engine_entered_ += 1; 458 } 459 460 XmppEngineImpl::EnterExit::~EnterExit() { 461 XmppEngineImpl* engine = engine_; 462 463 engine->engine_entered_ -= 1; 464 465 bool closing = (engine->state_ != state_ && 466 engine->state_ == STATE_CLOSED); 467 bool flushing = closing || (engine->engine_entered_ == 0); 468 469 if (engine->output_handler_ && flushing) { 470 std::string output = engine->output_->str(); 471 if (output.length() > 0) 472 engine->output_handler_->WriteOutput(output.c_str(), output.length()); 473 engine->output_->str(""); 474 475 if (closing) { 476 engine->output_handler_->CloseConnection(); 477 engine->output_handler_ = 0; 478 } 479 } 480 481 if (engine->engine_entered_) 482 return; 483 484 if (engine->raised_reset_) { 485 engine->stanzaParser_.Reset(); 486 engine->raised_reset_ = false; 487 } 488 489 if (engine->session_handler_) { 490 if (engine->state_ != state_) 491 engine->session_handler_->OnStateChange(engine->state_); 492 // Note: Handling of OnStateChange(CLOSED) should allow for the 493 // deletion of the engine, so no members should be accessed 494 // after this line. 495 } 496 } 497 498 } 499