• 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 #define TRACK_ARRAY_ALLOC_PROBLEM
29 
30 #include <vector>
31 #include <sstream>
32 #include <algorithm>
33 #include "talk/xmllite/xmlelement.h"
34 #include "talk/base/common.h"
35 #include "talk/xmpp/xmppengineimpl.h"
36 #include "talk/xmpp/xmpplogintask.h"
37 #include "talk/xmpp/constants.h"
38 #include "talk/xmllite/xmlprinter.h"
39 #include "talk/xmpp/saslhandler.h"
40 
41 namespace buzz {
42 
43 static const std::string XMPP_CLIENT_NAMESPACES[] = {
44   "stream", "http://etherx.jabber.org/streams",
45   "", "jabber:client",
46 };
47 
48 static const size_t XMPP_CLIENT_NAMESPACES_LEN = 4;
49 
Create()50 XmppEngine * XmppEngine::Create() {
51   return new XmppEngineImpl();
52 }
53 
54 
XmppEngineImpl()55 XmppEngineImpl::XmppEngineImpl() :
56     stanzaParseHandler_(this),
57     stanzaParser_(&stanzaParseHandler_),
58     engine_entered_(0),
59     user_jid_(JID_EMPTY),
60     password_(),
61     requested_resource_(STR_EMPTY),
62     tls_needed_(true),
63     login_task_(new XmppLoginTask(this)),
64     next_id_(0),
65     bound_jid_(JID_EMPTY),
66     state_(STATE_START),
67     encrypted_(false),
68     error_code_(ERROR_NONE),
69     subcode_(0),
70     stream_error_(NULL),
71     raised_reset_(false),
72     output_handler_(NULL),
73     session_handler_(NULL),
74     iq_entries_(new IqEntryVector()),
75     sasl_handler_(NULL),
76     output_(new std::stringstream()) {
77   for (int i = 0; i < HL_COUNT; i+= 1) {
78     stanza_handlers_[i].reset(new StanzaHandlerVector());
79   }
80 }
81 
~XmppEngineImpl()82 XmppEngineImpl::~XmppEngineImpl() {
83   DeleteIqCookies();
84 }
85 
86 XmppReturnStatus
SetOutputHandler(XmppOutputHandler * output_handler)87 XmppEngineImpl::SetOutputHandler(XmppOutputHandler* output_handler) {
88   if (state_ != STATE_START)
89     return XMPP_RETURN_BADSTATE;
90 
91   output_handler_ = output_handler;
92 
93   return XMPP_RETURN_OK;
94 }
95 
96 XmppReturnStatus
SetSessionHandler(XmppSessionHandler * session_handler)97 XmppEngineImpl::SetSessionHandler(XmppSessionHandler* session_handler) {
98   if (state_ != STATE_START)
99     return XMPP_RETURN_BADSTATE;
100 
101   session_handler_ = session_handler;
102 
103   return XMPP_RETURN_OK;
104 }
105 
106 XmppReturnStatus
HandleInput(const char * bytes,size_t len)107 XmppEngineImpl::HandleInput(const char * bytes, size_t len) {
108   if (state_ < STATE_OPENING || state_ > STATE_OPEN)
109     return XMPP_RETURN_BADSTATE;
110 
111   EnterExit ee(this);
112 
113   // TODO: The return value of the xml parser is not checked.
114   stanzaParser_.Parse(bytes, len, false);
115 
116   return XMPP_RETURN_OK;
117 }
118 
119 XmppReturnStatus
ConnectionClosed(int subcode)120 XmppEngineImpl::ConnectionClosed(int subcode) {
121   if (state_ != STATE_CLOSED) {
122     EnterExit ee(this);
123     // If told that connection closed and not already closed,
124     // then connection was unpexectedly dropped.
125     if (subcode) {
126       SignalError(ERROR_SOCKET, subcode);
127     } else {
128       SignalError(ERROR_CONNECTION_CLOSED, 0);  // no subcode
129     }
130   }
131   return XMPP_RETURN_OK;
132 }
133 
134 XmppReturnStatus
SetUseTls(bool useTls)135 XmppEngineImpl::SetUseTls(bool useTls) {
136   if (state_ != STATE_START)
137     return XMPP_RETURN_BADSTATE;
138 
139   tls_needed_ = useTls;
140 
141   return XMPP_RETURN_OK;
142 }
143 
144 XmppReturnStatus
SetTlsServer(const std::string & tls_server_hostname,const std::string & tls_server_domain)145 XmppEngineImpl::SetTlsServer(const std::string & tls_server_hostname,
146                              const std::string & tls_server_domain) {
147   if (state_ != STATE_START)
148     return XMPP_RETURN_BADSTATE;
149 
150   tls_server_hostname_ = tls_server_hostname;
151   tls_server_domain_= tls_server_domain;
152 
153   return XMPP_RETURN_OK;
154 }
155 
156 bool
GetUseTls()157 XmppEngineImpl::GetUseTls() {
158   return tls_needed_;
159 }
160 
161 XmppReturnStatus
SetUser(const Jid & jid)162 XmppEngineImpl::SetUser(const Jid & jid) {
163   if (state_ != STATE_START)
164     return XMPP_RETURN_BADSTATE;
165 
166   user_jid_ = jid;
167 
168   return XMPP_RETURN_OK;
169 }
170 
171 const Jid &
GetUser()172 XmppEngineImpl::GetUser() {
173   return user_jid_;
174 }
175 
176 XmppReturnStatus
SetSaslHandler(SaslHandler * sasl_handler)177 XmppEngineImpl::SetSaslHandler(SaslHandler * sasl_handler) {
178   if (state_ != STATE_START)
179     return XMPP_RETURN_BADSTATE;
180 
181   sasl_handler_.reset(sasl_handler);
182   return XMPP_RETURN_OK;
183 }
184 
185 XmppReturnStatus
SetRequestedResource(const std::string & resource)186 XmppEngineImpl::SetRequestedResource(const std::string & resource) {
187   if (state_ != STATE_START)
188     return XMPP_RETURN_BADSTATE;
189 
190   requested_resource_ = resource;
191 
192   return XMPP_RETURN_OK;
193 }
194 
195 const std::string &
GetRequestedResource()196 XmppEngineImpl::GetRequestedResource() {
197   return requested_resource_;
198 }
199 
200 XmppReturnStatus
AddStanzaHandler(XmppStanzaHandler * stanza_handler,XmppEngine::HandlerLevel level)201 XmppEngineImpl::AddStanzaHandler(XmppStanzaHandler * stanza_handler,
202                                  XmppEngine::HandlerLevel level) {
203   if (state_ == STATE_CLOSED)
204     return XMPP_RETURN_BADSTATE;
205 
206   stanza_handlers_[level]->push_back(stanza_handler);
207 
208   return XMPP_RETURN_OK;
209 }
210 
211 XmppReturnStatus
RemoveStanzaHandler(XmppStanzaHandler * stanza_handler)212 XmppEngineImpl::RemoveStanzaHandler(XmppStanzaHandler * stanza_handler) {
213 
214   bool found = false;
215 
216   for (int level = 0; level < HL_COUNT; level += 1) {
217     StanzaHandlerVector::iterator new_end =
218       std::remove(stanza_handlers_[level]->begin(),
219       stanza_handlers_[level]->end(),
220       stanza_handler);
221 
222     if (new_end != stanza_handlers_[level]->end()) {
223       stanza_handlers_[level]->erase(new_end, stanza_handlers_[level]->end());
224       found = true;
225     }
226   }
227 
228   if (!found) {
229     return XMPP_RETURN_BADARGUMENT;
230   }
231 
232   return XMPP_RETURN_OK;
233 }
234 
235 XmppReturnStatus
Connect()236 XmppEngineImpl::Connect() {
237   if (state_ != STATE_START)
238     return XMPP_RETURN_BADSTATE;
239 
240   EnterExit ee(this);
241 
242   // get the login task started
243   state_ = STATE_OPENING;
244   if (login_task_.get()) {
245     login_task_->IncomingStanza(NULL, false);
246     if (login_task_->IsDone())
247       login_task_.reset();
248   }
249 
250   return XMPP_RETURN_OK;
251 }
252 
253 XmppReturnStatus
SendStanza(const XmlElement * element)254 XmppEngineImpl::SendStanza(const XmlElement * element) {
255   if (state_ == STATE_CLOSED)
256     return XMPP_RETURN_BADSTATE;
257 
258   EnterExit ee(this);
259 
260   if (login_task_.get()) {
261     // still handshaking - then outbound stanzas are queued
262     login_task_->OutgoingStanza(element);
263   } else {
264     // handshake done - send straight through
265     InternalSendStanza(element);
266   }
267 
268   return XMPP_RETURN_OK;
269 }
270 
271 XmppReturnStatus
SendRaw(const std::string & text)272 XmppEngineImpl::SendRaw(const std::string & text) {
273   if (state_ == STATE_CLOSED || login_task_.get())
274     return XMPP_RETURN_BADSTATE;
275 
276   EnterExit ee(this);
277 
278   (*output_) << text;
279 
280   return XMPP_RETURN_OK;
281 }
282 
283 std::string
NextId()284 XmppEngineImpl::NextId() {
285   std::stringstream ss;
286   ss << next_id_++;
287   return ss.str();
288 }
289 
290 XmppReturnStatus
Disconnect()291 XmppEngineImpl::Disconnect() {
292 
293   if (state_ != STATE_CLOSED) {
294     EnterExit ee(this);
295     if (state_ == STATE_OPEN)
296       *output_ << "</stream:stream>";
297     state_ = STATE_CLOSED;
298   }
299 
300   return XMPP_RETURN_OK;
301 }
302 
303 void
IncomingStart(const XmlElement * pelStart)304 XmppEngineImpl::IncomingStart(const XmlElement * pelStart) {
305   if (HasError() || raised_reset_)
306     return;
307 
308   if (login_task_.get()) {
309     // start-stream should go to login task
310     login_task_->IncomingStanza(pelStart, true);
311     if (login_task_->IsDone())
312       login_task_.reset();
313   }
314   else {
315     // if not logging in, it's an error to see a start
316     SignalError(ERROR_XML, 0);
317   }
318 }
319 
320 void
IncomingStanza(const XmlElement * stanza)321 XmppEngineImpl::IncomingStanza(const XmlElement * stanza) {
322   if (HasError() || raised_reset_)
323     return;
324 
325   if (stanza->Name() == QN_STREAM_ERROR) {
326     // Explicit XMPP stream error
327     SignalStreamError(stanza);
328   } else if (login_task_.get()) {
329     // Handle login handshake
330     login_task_->IncomingStanza(stanza, false);
331     if (login_task_->IsDone())
332       login_task_.reset();
333   } else if (HandleIqResponse(stanza)) {
334     // iq is handled by above call
335   } else {
336     // give every "peek" handler a shot at all stanzas
337     for (size_t i = 0; i < stanza_handlers_[HL_PEEK]->size(); i += 1) {
338       (*stanza_handlers_[HL_PEEK])[i]->HandleStanza(stanza);
339     }
340 
341     // give other handlers a shot in precedence order, stopping after handled
342     for (int level = HL_SINGLE; level <= HL_ALL; level += 1) {
343       for (size_t i = 0; i < stanza_handlers_[level]->size(); i += 1) {
344         if ((*stanza_handlers_[level])[i]->HandleStanza(stanza))
345           goto Handled;
346       }
347     }
348 
349     // If nobody wants to handle a stanza then send back an error.
350     // Only do this for IQ stanzas as messages should probably just be dropped
351     // and presence stanzas should certainly be dropped.
352     std::string type = stanza->Attr(QN_TYPE);
353     if (stanza->Name() == QN_IQ &&
354         !(type == "error" || type == "result")) {
355       SendStanzaError(stanza, XSE_FEATURE_NOT_IMPLEMENTED, STR_EMPTY);
356     }
357   }
358   Handled:
359     ; // handled - we're done
360 }
361 
362 void
IncomingEnd(bool isError)363 XmppEngineImpl::IncomingEnd(bool isError) {
364   if (HasError() || raised_reset_)
365     return;
366 
367   SignalError(isError ? ERROR_XML : ERROR_DOCUMENT_CLOSED, 0);
368 }
369 
370 void
InternalSendStart(const std::string & to)371 XmppEngineImpl::InternalSendStart(const std::string & to) {
372   std::string hostname = tls_server_hostname_;
373   if (hostname.empty()) {
374     hostname = to;
375   }
376 
377   // If not language is specified, the spec says use *
378   std::string lang = lang_;
379   if (lang.length() == 0)
380     lang = "*";
381 
382   // send stream-beginning
383   // note, we put a \r\n at tne end fo the first line to cause non-XMPP
384   // line-oriented servers (e.g., Apache) to reveal themselves more quickly.
385   *output_ << "<stream:stream to=\"" << hostname << "\" "
386            << "xml:lang=\"" << lang << "\" "
387            << "version=\"1.0\" "
388            << "xmlns:stream=\"http://etherx.jabber.org/streams\" "
389            << "xmlns=\"jabber:client\">\r\n";
390 }
391 
392 void
InternalSendStanza(const XmlElement * element)393 XmppEngineImpl::InternalSendStanza(const XmlElement * element) {
394   // It should really never be necessary to set a FROM attribute on a stanza.
395   // It is implied by the bind on the stream and if you get it wrong
396   // (by flipping from/to on a message?) the server will close the stream.
397   ASSERT(!element->HasAttr(QN_FROM));
398 
399   // TODO: consider caching the XmlPrinter
400   XmlPrinter::PrintXml(output_.get(), element,
401             XMPP_CLIENT_NAMESPACES, XMPP_CLIENT_NAMESPACES_LEN);
402 }
403 
404 std::string
ChooseBestSaslMechanism(const std::vector<std::string> & mechanisms,bool encrypted)405 XmppEngineImpl::ChooseBestSaslMechanism(const std::vector<std::string> & mechanisms, bool encrypted) {
406   return sasl_handler_->ChooseBestSaslMechanism(mechanisms, encrypted);
407 }
408 
409 SaslMechanism *
GetSaslMechanism(const std::string & name)410 XmppEngineImpl::GetSaslMechanism(const std::string & name) {
411   return sasl_handler_->CreateSaslMechanism(name);
412 }
413 
414 void
SignalBound(const Jid & fullJid)415 XmppEngineImpl::SignalBound(const Jid & fullJid) {
416   if (state_ == STATE_OPENING) {
417     bound_jid_ = fullJid;
418     state_ = STATE_OPEN;
419   }
420 }
421 
422 void
SignalStreamError(const XmlElement * pelStreamError)423 XmppEngineImpl::SignalStreamError(const XmlElement * pelStreamError) {
424   if (state_ != STATE_CLOSED) {
425     stream_error_.reset(new XmlElement(*pelStreamError));
426     SignalError(ERROR_STREAM, 0);
427   }
428 }
429 
430 void
SignalError(Error errorCode,int subCode)431 XmppEngineImpl::SignalError(Error errorCode, int subCode) {
432   if (state_ != STATE_CLOSED) {
433     error_code_ = errorCode;
434     subcode_ = subCode;
435     state_ = STATE_CLOSED;
436   }
437 }
438 
439 bool
HasError()440 XmppEngineImpl::HasError() {
441   return error_code_ != ERROR_NONE;
442 }
443 
444 void
StartTls(const std::string & domain)445 XmppEngineImpl::StartTls(const std::string & domain) {
446   if (output_handler_) {
447     output_handler_->StartTls(
448       tls_server_domain_.empty() ? domain : tls_server_domain_);
449     encrypted_ = true;
450   }
451 }
452 
EnterExit(XmppEngineImpl * engine)453 XmppEngineImpl::EnterExit::EnterExit(XmppEngineImpl* engine)
454   : engine_(engine),
455   state_(engine->state_),
456   error_(engine->error_code_) {
457   engine->engine_entered_ += 1;
458 }
459 
~EnterExit()460 XmppEngineImpl::EnterExit::~EnterExit()  {
461  XmppEngineImpl* engine = engine_;
462 
463  engine->engine_entered_ -= 1;
464 
465  bool closing = (engine->state_ != state_ &&
466        engine->state_ == STATE_CLOSED);
467  bool flushing = closing || (engine->engine_entered_ == 0);
468 
469  if (engine->output_handler_ && flushing) {
470    std::string output = engine->output_->str();
471    if (output.length() > 0)
472      engine->output_handler_->WriteOutput(output.c_str(), output.length());
473    engine->output_->str("");
474 
475    if (closing) {
476      engine->output_handler_->CloseConnection();
477      engine->output_handler_ = 0;
478    }
479  }
480 
481  if (engine->engine_entered_)
482    return;
483 
484  if (engine->raised_reset_) {
485    engine->stanzaParser_.Reset();
486    engine->raised_reset_ = false;
487  }
488 
489  if (engine->session_handler_) {
490    if (engine->state_ != state_)
491      engine->session_handler_->OnStateChange(engine->state_);
492      // Note: Handling of OnStateChange(CLOSED) should allow for the
493      // deletion of the engine, so no members should be accessed
494      // after this line.
495  }
496 }
497 
498 }
499