3 * Copyright 2004--2005, Google Inc.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
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.
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.
28 #include "webrtc/libjingle/xmpp/xmppengineimpl.h"
34 #include "webrtc/libjingle/xmllite/xmlelement.h"
35 #include "webrtc/libjingle/xmllite/xmlprinter.h"
36 #include "webrtc/libjingle/xmpp/constants.h"
37 #include "webrtc/libjingle/xmpp/saslhandler.h"
38 #include "webrtc/libjingle/xmpp/xmpplogintask.h"
39 #include "webrtc/base/common.h"
43 XmppEngine* XmppEngine::Create() {
44 return new XmppEngineImpl();
48 XmppEngineImpl::XmppEngineImpl()
49 : stanza_parse_handler_(this),
50 stanza_parser_(&stanza_parse_handler_),
53 requested_resource_(STR_EMPTY),
54 tls_option_(buzz::TLS_REQUIRED),
55 login_task_(new XmppLoginTask(this)),
59 error_code_(ERROR_NONE),
63 output_handler_(NULL),
64 session_handler_(NULL),
65 iq_entries_(new IqEntryVector()),
67 output_(new std::stringstream()) {
68 for (int i = 0; i < HL_COUNT; i+= 1) {
69 stanza_handlers_[i].reset(new StanzaHandlerVector());
72 // Add XMPP namespaces to XML namespaces stack.
73 xmlns_stack_.AddXmlns("stream", "http://etherx.jabber.org/streams");
74 xmlns_stack_.AddXmlns("", "jabber:client");
77 XmppEngineImpl::~XmppEngineImpl() {
81 XmppReturnStatus XmppEngineImpl::SetOutputHandler(
82 XmppOutputHandler* output_handler) {
83 if (state_ != STATE_START)
84 return XMPP_RETURN_BADSTATE;
86 output_handler_ = output_handler;
88 return XMPP_RETURN_OK;
91 XmppReturnStatus XmppEngineImpl::SetSessionHandler(
92 XmppSessionHandler* session_handler) {
93 if (state_ != STATE_START)
94 return XMPP_RETURN_BADSTATE;
96 session_handler_ = session_handler;
98 return XMPP_RETURN_OK;
101 XmppReturnStatus XmppEngineImpl::HandleInput(
102 const char* bytes, size_t len) {
103 if (state_ < STATE_OPENING || state_ > STATE_OPEN)
104 return XMPP_RETURN_BADSTATE;
108 // TODO: The return value of the xml parser is not checked.
109 stanza_parser_.Parse(bytes, len, false);
111 return XMPP_RETURN_OK;
114 XmppReturnStatus XmppEngineImpl::ConnectionClosed(int subcode) {
115 if (state_ != STATE_CLOSED) {
117 // If told that connection closed and not already closed,
118 // then connection was unpexectedly dropped.
120 SignalError(ERROR_SOCKET, subcode);
122 SignalError(ERROR_CONNECTION_CLOSED, 0); // no subcode
125 return XMPP_RETURN_OK;
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;
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;
141 tls_server_hostname_ = tls_server_hostname;
142 tls_server_domain_= tls_server_domain;
144 return XMPP_RETURN_OK;
147 TlsOptions XmppEngineImpl::GetTls() {
151 XmppReturnStatus XmppEngineImpl::SetUser(const Jid& jid) {
152 if (state_ != STATE_START)
153 return XMPP_RETURN_BADSTATE;
157 return XMPP_RETURN_OK;
160 const Jid& XmppEngineImpl::GetUser() {
164 XmppReturnStatus XmppEngineImpl::SetSaslHandler(SaslHandler* sasl_handler) {
165 if (state_ != STATE_START)
166 return XMPP_RETURN_BADSTATE;
168 sasl_handler_.reset(sasl_handler);
169 return XMPP_RETURN_OK;
172 XmppReturnStatus XmppEngineImpl::SetRequestedResource(
173 const std::string& resource) {
174 if (state_ != STATE_START)
175 return XMPP_RETURN_BADSTATE;
177 requested_resource_ = resource;
179 return XMPP_RETURN_OK;
182 const std::string& XmppEngineImpl::GetRequestedResource() {
183 return requested_resource_;
186 XmppReturnStatus XmppEngineImpl::AddStanzaHandler(
187 XmppStanzaHandler* stanza_handler,
188 XmppEngine::HandlerLevel level) {
189 if (state_ == STATE_CLOSED)
190 return XMPP_RETURN_BADSTATE;
192 stanza_handlers_[level]->push_back(stanza_handler);
194 return XMPP_RETURN_OK;
197 XmppReturnStatus XmppEngineImpl::RemoveStanzaHandler(
198 XmppStanzaHandler* stanza_handler) {
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(),
207 if (new_end != stanza_handlers_[level]->end()) {
208 stanza_handlers_[level]->erase(new_end, stanza_handlers_[level]->end());
214 return XMPP_RETURN_BADARGUMENT;
216 return XMPP_RETURN_OK;
219 XmppReturnStatus XmppEngineImpl::Connect() {
220 if (state_ != STATE_START)
221 return XMPP_RETURN_BADSTATE;
225 // get the login task started
226 state_ = STATE_OPENING;
228 login_task_->IncomingStanza(NULL, false);
229 if (login_task_->IsDone())
233 return XMPP_RETURN_OK;
236 XmppReturnStatus XmppEngineImpl::SendStanza(const XmlElement* element) {
237 if (state_ == STATE_CLOSED)
238 return XMPP_RETURN_BADSTATE;
243 // still handshaking - then outbound stanzas are queued
244 login_task_->OutgoingStanza(element);
246 // handshake done - send straight through
247 InternalSendStanza(element);
250 return XMPP_RETURN_OK;
253 XmppReturnStatus XmppEngineImpl::SendRaw(const std::string& text) {
254 if (state_ == STATE_CLOSED || login_task_)
255 return XMPP_RETURN_BADSTATE;
261 return XMPP_RETURN_OK;
264 std::string XmppEngineImpl::NextId() {
265 std::stringstream ss;
270 XmppReturnStatus XmppEngineImpl::Disconnect() {
271 if (state_ != STATE_CLOSED) {
273 if (state_ == STATE_OPEN)
274 *output_ << "</stream:stream>";
275 state_ = STATE_CLOSED;
278 return XMPP_RETURN_OK;
281 void XmppEngineImpl::IncomingStart(const XmlElement* start) {
282 if (HasError() || raised_reset_)
286 // start-stream should go to login task
287 login_task_->IncomingStanza(start, true);
288 if (login_task_->IsDone())
292 // if not logging in, it's an error to see a start
293 SignalError(ERROR_XML, 0);
297 void XmppEngineImpl::IncomingStanza(const XmlElement* stanza) {
298 if (HasError() || raised_reset_)
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())
309 } else if (HandleIqResponse(stanza)) {
310 // iq is handled by above call
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);
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))
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);
336 void XmppEngineImpl::IncomingEnd(bool isError) {
337 if (HasError() || raised_reset_)
340 SignalError(isError ? ERROR_XML : ERROR_DOCUMENT_CLOSED, 0);
343 void XmppEngineImpl::InternalSendStart(const std::string& to) {
344 std::string hostname = tls_server_hostname_;
345 if (hostname.empty())
348 // If not language is specified, the spec says use *
349 std::string lang = lang_;
350 if (lang.length() == 0)
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";
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));
369 XmlPrinter::PrintXml(output_.get(), element, &xmlns_stack_);
372 std::string XmppEngineImpl::ChooseBestSaslMechanism(
373 const std::vector<std::string>& mechanisms, bool encrypted) {
374 return sasl_handler_->ChooseBestSaslMechanism(mechanisms, encrypted);
377 SaslMechanism* XmppEngineImpl::GetSaslMechanism(const std::string& name) {
378 return sasl_handler_->CreateSaslMechanism(name);
381 void XmppEngineImpl::SignalBound(const Jid& fullJid) {
382 if (state_ == STATE_OPENING) {
383 bound_jid_ = fullJid;
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);
395 void XmppEngineImpl::SignalError(Error error_code, int sub_code) {
396 if (state_ != STATE_CLOSED) {
397 error_code_ = error_code;
399 state_ = STATE_CLOSED;
403 bool XmppEngineImpl::HasError() {
404 return error_code_ != ERROR_NONE;
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_);
419 XmppEngineImpl::EnterExit::EnterExit(XmppEngineImpl* engine)
421 state_(engine->state_) {
422 engine->engine_entered_ += 1;
425 XmppEngineImpl::EnterExit::~EnterExit() {
426 XmppEngineImpl* engine = engine_;
428 engine->engine_entered_ -= 1;
430 bool closing = (engine->state_ != state_ &&
431 engine->state_ == STATE_CLOSED);
432 bool flushing = closing || (engine->engine_entered_ == 0);
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("");
441 engine->output_handler_->CloseConnection();
442 engine->output_handler_ = 0;
446 if (engine->engine_entered_)
449 if (engine->raised_reset_) {
450 engine->stanza_parser_.Reset();
451 engine->raised_reset_ = false;
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