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