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 "webrtc/libjingle/xmllite/xmlelement.h"
35#include "webrtc/libjingle/xmllite/xmlprinter.h"
36#include "talk/xmpp/constants.h"
37#include "talk/xmpp/saslhandler.h"
38#include "talk/xmpp/xmpplogintask.h"
39#include "webrtc/base/common.h"
40
41namespace buzz {
42
43XmppEngine* XmppEngine::Create() {
44  return new XmppEngineImpl();
45}
46
47
48XmppEngineImpl::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
77XmppEngineImpl::~XmppEngineImpl() {
78  DeleteIqCookies();
79}
80
81XmppReturnStatus 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
91XmppReturnStatus 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
101XmppReturnStatus 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
114XmppReturnStatus 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
128XmppReturnStatus 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
135XmppReturnStatus 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
147TlsOptions XmppEngineImpl::GetTls() {
148  return tls_option_;
149}
150
151XmppReturnStatus 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
160const Jid& XmppEngineImpl::GetUser() {
161  return user_jid_;
162}
163
164XmppReturnStatus 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
172XmppReturnStatus 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
182const std::string& XmppEngineImpl::GetRequestedResource() {
183  return requested_resource_;
184}
185
186XmppReturnStatus 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
197XmppReturnStatus 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
219XmppReturnStatus 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
236XmppReturnStatus 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
253XmppReturnStatus 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
264std::string XmppEngineImpl::NextId() {
265  std::stringstream ss;
266  ss << next_id_++;
267  return ss.str();
268}
269
270XmppReturnStatus 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
281void 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
297void 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
336void XmppEngineImpl::IncomingEnd(bool isError) {
337  if (HasError() || raised_reset_)
338    return;
339
340  SignalError(isError ? ERROR_XML : ERROR_DOCUMENT_CLOSED, 0);
341}
342
343void 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
363void 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
372std::string XmppEngineImpl::ChooseBestSaslMechanism(
373    const std::vector<std::string>& mechanisms, bool encrypted) {
374  return sasl_handler_->ChooseBestSaslMechanism(mechanisms, encrypted);
375}
376
377SaslMechanism* XmppEngineImpl::GetSaslMechanism(const std::string& name) {
378  return sasl_handler_->CreateSaslMechanism(name);
379}
380
381void XmppEngineImpl::SignalBound(const Jid& fullJid) {
382  if (state_ == STATE_OPENING) {
383    bound_jid_ = fullJid;
384    state_ = STATE_OPEN;
385  }
386}
387
388void 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
395void 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
403bool XmppEngineImpl::HasError() {
404  return error_code_ != ERROR_NONE;
405}
406
407void 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
419XmppEngineImpl::EnterExit::EnterExit(XmppEngineImpl* engine)
420    : engine_(engine),
421      state_(engine->state_) {
422  engine->engine_entered_ += 1;
423}
424
425XmppEngineImpl::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