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