• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
Create()43 XmppEngine* XmppEngine::Create() {
44   return new XmppEngineImpl();
45 }
46 
47 
XmppEngineImpl()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 
~XmppEngineImpl()77 XmppEngineImpl::~XmppEngineImpl() {
78   DeleteIqCookies();
79 }
80 
SetOutputHandler(XmppOutputHandler * output_handler)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 
SetSessionHandler(XmppSessionHandler * session_handler)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 
HandleInput(const char * bytes,size_t len)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 
ConnectionClosed(int subcode)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 
SetTls(TlsOptions use_tls)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 
SetTlsServer(const std::string & tls_server_hostname,const std::string & tls_server_domain)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 
GetTls()147 TlsOptions XmppEngineImpl::GetTls() {
148   return tls_option_;
149 }
150 
SetUser(const Jid & jid)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 
GetUser()160 const Jid& XmppEngineImpl::GetUser() {
161   return user_jid_;
162 }
163 
SetSaslHandler(SaslHandler * sasl_handler)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 
SetRequestedResource(const std::string & resource)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 
GetRequestedResource()182 const std::string& XmppEngineImpl::GetRequestedResource() {
183   return requested_resource_;
184 }
185 
AddStanzaHandler(XmppStanzaHandler * stanza_handler,XmppEngine::HandlerLevel level)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 
RemoveStanzaHandler(XmppStanzaHandler * stanza_handler)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 
Connect()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 
SendStanza(const XmlElement * element)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 
SendRaw(const std::string & text)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 
NextId()264 std::string XmppEngineImpl::NextId() {
265   std::stringstream ss;
266   ss << next_id_++;
267   return ss.str();
268 }
269 
Disconnect()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 
IncomingStart(const XmlElement * start)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 
IncomingStanza(const XmlElement * stanza)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 
IncomingEnd(bool isError)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 
InternalSendStart(const std::string & to)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 
InternalSendStanza(const XmlElement * element)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 
ChooseBestSaslMechanism(const std::vector<std::string> & mechanisms,bool encrypted)372 std::string XmppEngineImpl::ChooseBestSaslMechanism(
373     const std::vector<std::string>& mechanisms, bool encrypted) {
374   return sasl_handler_->ChooseBestSaslMechanism(mechanisms, encrypted);
375 }
376 
GetSaslMechanism(const std::string & name)377 SaslMechanism* XmppEngineImpl::GetSaslMechanism(const std::string& name) {
378   return sasl_handler_->CreateSaslMechanism(name);
379 }
380 
SignalBound(const Jid & fullJid)381 void XmppEngineImpl::SignalBound(const Jid& fullJid) {
382   if (state_ == STATE_OPENING) {
383     bound_jid_ = fullJid;
384     state_ = STATE_OPEN;
385   }
386 }
387 
SignalStreamError(const XmlElement * stream_error)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 
SignalError(Error error_code,int sub_code)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 
HasError()403 bool XmppEngineImpl::HasError() {
404   return error_code_ != ERROR_NONE;
405 }
406 
StartTls(const std::string & domain)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 
EnterExit(XmppEngineImpl * engine)419 XmppEngineImpl::EnterExit::EnterExit(XmppEngineImpl* engine)
420     : engine_(engine),
421       state_(engine->state_) {
422   engine->engine_entered_ += 1;
423 }
424 
~EnterExit()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