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 24namespace buzz { 25 26XmppEngine* XmppEngine::Create() { 27 return new XmppEngineImpl(); 28} 29 30 31XmppEngineImpl::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 60XmppEngineImpl::~XmppEngineImpl() { 61 DeleteIqCookies(); 62} 63 64XmppReturnStatus 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 74XmppReturnStatus 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 84XmppReturnStatus 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 97XmppReturnStatus 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 111XmppReturnStatus 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 118XmppReturnStatus 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 130TlsOptions XmppEngineImpl::GetTls() { 131 return tls_option_; 132} 133 134XmppReturnStatus 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 143const Jid& XmppEngineImpl::GetUser() { 144 return user_jid_; 145} 146 147XmppReturnStatus 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 155XmppReturnStatus 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 165const std::string& XmppEngineImpl::GetRequestedResource() { 166 return requested_resource_; 167} 168 169XmppReturnStatus 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 180XmppReturnStatus 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 202XmppReturnStatus 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 219XmppReturnStatus 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 236XmppReturnStatus 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 247std::string XmppEngineImpl::NextId() { 248 std::stringstream ss; 249 ss << next_id_++; 250 return ss.str(); 251} 252 253XmppReturnStatus 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 264void 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 280void 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 319void XmppEngineImpl::IncomingEnd(bool isError) { 320 if (HasError() || raised_reset_) 321 return; 322 323 SignalError(isError ? ERROR_XML : ERROR_DOCUMENT_CLOSED, 0); 324} 325 326void 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 346void 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 355std::string XmppEngineImpl::ChooseBestSaslMechanism( 356 const std::vector<std::string>& mechanisms, bool encrypted) { 357 return sasl_handler_->ChooseBestSaslMechanism(mechanisms, encrypted); 358} 359 360SaslMechanism* XmppEngineImpl::GetSaslMechanism(const std::string& name) { 361 return sasl_handler_->CreateSaslMechanism(name); 362} 363 364void XmppEngineImpl::SignalBound(const Jid& fullJid) { 365 if (state_ == STATE_OPENING) { 366 bound_jid_ = fullJid; 367 state_ = STATE_OPEN; 368 } 369} 370 371void 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 378void 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 386bool XmppEngineImpl::HasError() { 387 return error_code_ != ERROR_NONE; 388} 389 390void 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 402XmppEngineImpl::EnterExit::EnterExit(XmppEngineImpl* engine) 403 : engine_(engine), 404 state_(engine->state_) { 405 engine->engine_entered_ += 1; 406} 407 408XmppEngineImpl::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