• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
24 namespace buzz {
25 
Create()26 XmppEngine* XmppEngine::Create() {
27   return new XmppEngineImpl();
28 }
29 
30 
XmppEngineImpl()31 XmppEngineImpl::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 
~XmppEngineImpl()60 XmppEngineImpl::~XmppEngineImpl() {
61   DeleteIqCookies();
62 }
63 
SetOutputHandler(XmppOutputHandler * output_handler)64 XmppReturnStatus 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 
SetSessionHandler(XmppSessionHandler * session_handler)74 XmppReturnStatus 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 
HandleInput(const char * bytes,size_t len)84 XmppReturnStatus 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 
ConnectionClosed(int subcode)97 XmppReturnStatus 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 
SetTls(TlsOptions use_tls)111 XmppReturnStatus 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 
SetTlsServer(const std::string & tls_server_hostname,const std::string & tls_server_domain)118 XmppReturnStatus 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 
GetTls()130 TlsOptions XmppEngineImpl::GetTls() {
131   return tls_option_;
132 }
133 
SetUser(const Jid & jid)134 XmppReturnStatus 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 
GetUser()143 const Jid& XmppEngineImpl::GetUser() {
144   return user_jid_;
145 }
146 
SetSaslHandler(SaslHandler * sasl_handler)147 XmppReturnStatus 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 
SetRequestedResource(const std::string & resource)155 XmppReturnStatus 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 
GetRequestedResource()165 const std::string& XmppEngineImpl::GetRequestedResource() {
166   return requested_resource_;
167 }
168 
AddStanzaHandler(XmppStanzaHandler * stanza_handler,XmppEngine::HandlerLevel level)169 XmppReturnStatus 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 
RemoveStanzaHandler(XmppStanzaHandler * stanza_handler)180 XmppReturnStatus 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 
Connect()202 XmppReturnStatus 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 
SendStanza(const XmlElement * element)219 XmppReturnStatus 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 
SendRaw(const std::string & text)236 XmppReturnStatus 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 
NextId()247 std::string XmppEngineImpl::NextId() {
248   std::stringstream ss;
249   ss << next_id_++;
250   return ss.str();
251 }
252 
Disconnect()253 XmppReturnStatus 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 
IncomingStart(const XmlElement * start)264 void 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 
IncomingStanza(const XmlElement * stanza)280 void 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 
IncomingEnd(bool isError)319 void XmppEngineImpl::IncomingEnd(bool isError) {
320   if (HasError() || raised_reset_)
321     return;
322 
323   SignalError(isError ? ERROR_XML : ERROR_DOCUMENT_CLOSED, 0);
324 }
325 
InternalSendStart(const std::string & to)326 void 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 
InternalSendStanza(const XmlElement * element)346 void 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 
ChooseBestSaslMechanism(const std::vector<std::string> & mechanisms,bool encrypted)355 std::string XmppEngineImpl::ChooseBestSaslMechanism(
356     const std::vector<std::string>& mechanisms, bool encrypted) {
357   return sasl_handler_->ChooseBestSaslMechanism(mechanisms, encrypted);
358 }
359 
GetSaslMechanism(const std::string & name)360 SaslMechanism* XmppEngineImpl::GetSaslMechanism(const std::string& name) {
361   return sasl_handler_->CreateSaslMechanism(name);
362 }
363 
SignalBound(const Jid & fullJid)364 void XmppEngineImpl::SignalBound(const Jid& fullJid) {
365   if (state_ == STATE_OPENING) {
366     bound_jid_ = fullJid;
367     state_ = STATE_OPEN;
368   }
369 }
370 
SignalStreamError(const XmlElement * stream_error)371 void 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 
SignalError(Error error_code,int sub_code)378 void 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 
HasError()386 bool XmppEngineImpl::HasError() {
387   return error_code_ != ERROR_NONE;
388 }
389 
StartTls(const std::string & domain)390 void 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 
EnterExit(XmppEngineImpl * engine)402 XmppEngineImpl::EnterExit::EnterExit(XmppEngineImpl* engine)
403     : engine_(engine),
404       state_(engine->state_) {
405   engine->engine_entered_ += 1;
406 }
407 
~EnterExit()408 XmppEngineImpl::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