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