3
* Copyright 2004--2005, Google Inc.
5
* Redistribution and use in source and binary forms, with or without
6
* modification, are permitted provided that the following conditions are met:
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.
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.
28
#include "talk/xmpp/xmppengineimpl.h"
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"
43
XmppEngine* XmppEngine::Create() {
44
return new XmppEngineImpl();
48
XmppEngineImpl::XmppEngineImpl()
49
: stanza_parse_handler_(this),
50
stanza_parser_(&stanza_parse_handler_),
53
requested_resource_(STR_EMPTY),
54
tls_option_(buzz::TLS_REQUIRED),
55
login_task_(new XmppLoginTask(this)),
59
error_code_(ERROR_NONE),
63
output_handler_(NULL),
64
session_handler_(NULL),
65
iq_entries_(new IqEntryVector()),
67
output_(new std::stringstream()) {
68
for (int i = 0; i < HL_COUNT; i+= 1) {
69
stanza_handlers_[i].reset(new StanzaHandlerVector());
72
// Add XMPP namespaces to XML namespaces stack.
73
xmlns_stack_.AddXmlns("stream", "http://etherx.jabber.org/streams");
74
xmlns_stack_.AddXmlns("", "jabber:client");
77
XmppEngineImpl::~XmppEngineImpl() {
81
XmppReturnStatus XmppEngineImpl::SetOutputHandler(
82
XmppOutputHandler* output_handler) {
83
if (state_ != STATE_START)
84
return XMPP_RETURN_BADSTATE;
86
output_handler_ = output_handler;
88
return XMPP_RETURN_OK;
91
XmppReturnStatus XmppEngineImpl::SetSessionHandler(
92
XmppSessionHandler* session_handler) {
93
if (state_ != STATE_START)
94
return XMPP_RETURN_BADSTATE;
96
session_handler_ = session_handler;
98
return XMPP_RETURN_OK;
101
XmppReturnStatus XmppEngineImpl::HandleInput(
102
const char* bytes, size_t len) {
103
if (state_ < STATE_OPENING || state_ > STATE_OPEN)
104
return XMPP_RETURN_BADSTATE;
108
// TODO: The return value of the xml parser is not checked.
109
stanza_parser_.Parse(bytes, len, false);
111
return XMPP_RETURN_OK;
114
XmppReturnStatus XmppEngineImpl::ConnectionClosed(int subcode) {
115
if (state_ != STATE_CLOSED) {
117
// If told that connection closed and not already closed,
118
// then connection was unpexectedly dropped.
120
SignalError(ERROR_SOCKET, subcode);
122
SignalError(ERROR_CONNECTION_CLOSED, 0); // no subcode
125
return XMPP_RETURN_OK;
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;
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;
141
tls_server_hostname_ = tls_server_hostname;
142
tls_server_domain_= tls_server_domain;
144
return XMPP_RETURN_OK;
147
TlsOptions XmppEngineImpl::GetTls() {
151
XmppReturnStatus XmppEngineImpl::SetUser(const Jid& jid) {
152
if (state_ != STATE_START)
153
return XMPP_RETURN_BADSTATE;
157
return XMPP_RETURN_OK;
160
const Jid& XmppEngineImpl::GetUser() {
164
XmppReturnStatus XmppEngineImpl::SetSaslHandler(SaslHandler* sasl_handler) {
165
if (state_ != STATE_START)
166
return XMPP_RETURN_BADSTATE;
168
sasl_handler_.reset(sasl_handler);
169
return XMPP_RETURN_OK;
172
XmppReturnStatus XmppEngineImpl::SetRequestedResource(
173
const std::string& resource) {
174
if (state_ != STATE_START)
175
return XMPP_RETURN_BADSTATE;
177
requested_resource_ = resource;
179
return XMPP_RETURN_OK;
182
const std::string& XmppEngineImpl::GetRequestedResource() {
183
return requested_resource_;
186
XmppReturnStatus XmppEngineImpl::AddStanzaHandler(
187
XmppStanzaHandler* stanza_handler,
188
XmppEngine::HandlerLevel level) {
189
if (state_ == STATE_CLOSED)
190
return XMPP_RETURN_BADSTATE;
192
stanza_handlers_[level]->push_back(stanza_handler);
194
return XMPP_RETURN_OK;
197
XmppReturnStatus XmppEngineImpl::RemoveStanzaHandler(
198
XmppStanzaHandler* stanza_handler) {
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(),
207
if (new_end != stanza_handlers_[level]->end()) {
208
stanza_handlers_[level]->erase(new_end, stanza_handlers_[level]->end());
214
return XMPP_RETURN_BADARGUMENT;
216
return XMPP_RETURN_OK;
219
XmppReturnStatus XmppEngineImpl::Connect() {
220
if (state_ != STATE_START)
221
return XMPP_RETURN_BADSTATE;
225
// get the login task started
226
state_ = STATE_OPENING;
227
if (login_task_.get()) {
228
login_task_->IncomingStanza(NULL, false);
229
if (login_task_->IsDone())
233
return XMPP_RETURN_OK;
236
XmppReturnStatus XmppEngineImpl::SendStanza(const XmlElement* element) {
237
if (state_ == STATE_CLOSED)
238
return XMPP_RETURN_BADSTATE;
242
if (login_task_.get()) {
243
// still handshaking - then outbound stanzas are queued
244
login_task_->OutgoingStanza(element);
246
// handshake done - send straight through
247
InternalSendStanza(element);
250
return XMPP_RETURN_OK;
253
XmppReturnStatus XmppEngineImpl::SendRaw(const std::string& text) {
254
if (state_ == STATE_CLOSED || login_task_.get())
255
return XMPP_RETURN_BADSTATE;
261
return XMPP_RETURN_OK;
264
std::string XmppEngineImpl::NextId() {
265
std::stringstream ss;
270
XmppReturnStatus XmppEngineImpl::Disconnect() {
271
if (state_ != STATE_CLOSED) {
273
if (state_ == STATE_OPEN)
274
*output_ << "</stream:stream>";
275
state_ = STATE_CLOSED;
278
return XMPP_RETURN_OK;
281
void XmppEngineImpl::IncomingStart(const XmlElement* start) {
282
if (HasError() || raised_reset_)
285
if (login_task_.get()) {
286
// start-stream should go to login task
287
login_task_->IncomingStanza(start, true);
288
if (login_task_->IsDone())
292
// if not logging in, it's an error to see a start
293
SignalError(ERROR_XML, 0);
297
void XmppEngineImpl::IncomingStanza(const XmlElement* stanza) {
298
if (HasError() || raised_reset_)
301
if (stanza->Name() == QN_STREAM_ERROR) {
302
// Explicit XMPP stream error
303
SignalStreamError(stanza);
304
} else if (login_task_.get()) {
305
// Handle login handshake
306
login_task_->IncomingStanza(stanza, false);
307
if (login_task_->IsDone())
309
} else if (HandleIqResponse(stanza)) {
310
// iq is handled by above call
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);
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))
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);
336
void XmppEngineImpl::IncomingEnd(bool isError) {
337
if (HasError() || raised_reset_)
340
SignalError(isError ? ERROR_XML : ERROR_DOCUMENT_CLOSED, 0);
343
void XmppEngineImpl::InternalSendStart(const std::string& to) {
344
std::string hostname = tls_server_hostname_;
345
if (hostname.empty())
348
// If not language is specified, the spec says use *
349
std::string lang = lang_;
350
if (lang.length() == 0)
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";
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));
369
XmlPrinter::PrintXml(output_.get(), element, &xmlns_stack_);
372
std::string XmppEngineImpl::ChooseBestSaslMechanism(
373
const std::vector<std::string>& mechanisms, bool encrypted) {
374
return sasl_handler_->ChooseBestSaslMechanism(mechanisms, encrypted);
377
SaslMechanism* XmppEngineImpl::GetSaslMechanism(const std::string& name) {
378
return sasl_handler_->CreateSaslMechanism(name);
381
void XmppEngineImpl::SignalBound(const Jid& fullJid) {
382
if (state_ == STATE_OPENING) {
383
bound_jid_ = fullJid;
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);
395
void XmppEngineImpl::SignalError(Error error_code, int sub_code) {
396
if (state_ != STATE_CLOSED) {
397
error_code_ = error_code;
399
state_ = STATE_CLOSED;
403
bool XmppEngineImpl::HasError() {
404
return error_code_ != ERROR_NONE;
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_);
419
XmppEngineImpl::EnterExit::EnterExit(XmppEngineImpl* engine)
421
state_(engine->state_),
422
error_(engine->error_code_) {
423
engine->engine_entered_ += 1;
426
XmppEngineImpl::EnterExit::~EnterExit() {
427
XmppEngineImpl* engine = engine_;
429
engine->engine_entered_ -= 1;
431
bool closing = (engine->state_ != state_ &&
432
engine->state_ == STATE_CLOSED);
433
bool flushing = closing || (engine->engine_entered_ == 0);
435
if (engine->output_handler_ && flushing) {
436
std::string output = engine->output_->str();
437
if (output.length() > 0)
438
engine->output_handler_->WriteOutput(output.c_str(), output.length());
439
engine->output_->str("");
442
engine->output_handler_->CloseConnection();
443
engine->output_handler_ = 0;
447
if (engine->engine_entered_)
450
if (engine->raised_reset_) {
451
engine->stanza_parser_.Reset();
452
engine->raised_reset_ = false;
455
if (engine->session_handler_) {
456
if (engine->state_ != state_)
457
engine->session_handler_->OnStateChange(engine->state_);
458
// Note: Handling of OnStateChange(CLOSED) should allow for the
459
// deletion of the engine, so no members should be accessed