Home | History | Annotate | Download | only in xmpp
      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