~ubuntu-branches/debian/experimental/kopete/experimental

« back to all changes in this revision

Viewing changes to protocols/jabber/googletalk/libjingle/talk/xmpp/xmppengineimpl.cc

  • Committer: Package Import Robot
  • Author(s): Maximiliano Curia
  • Date: 2015-02-24 11:32:57 UTC
  • mfrom: (1.1.41 vivid)
  • Revision ID: package-import@ubuntu.com-20150224113257-gnupg4v7lzz18ij0
Tags: 4:14.12.2-1
* New upstream release (14.12.2).
* Bump Standards-Version to 3.9.6, no changes needed.

Show diffs side-by-side

added added

removed removed

Lines of Context:
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
 
 
43
 
XmppEngine* XmppEngine::Create() {
44
 
  return new XmppEngineImpl();
45
 
}
46
 
 
47
 
 
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_(NULL),
62
 
      raised_reset_(false),
63
 
      output_handler_(NULL),
64
 
      session_handler_(NULL),
65
 
      iq_entries_(new IqEntryVector()),
66
 
      sasl_handler_(NULL),
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
 
 
77
 
XmppEngineImpl::~XmppEngineImpl() {
78
 
  DeleteIqCookies();
79
 
}
80
 
 
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
 
 
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
 
 
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
 
 
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
 
 
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
 
 
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
 
 
147
 
TlsOptions XmppEngineImpl::GetTls() {
148
 
  return tls_option_;
149
 
}
150
 
 
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
 
 
160
 
const Jid& XmppEngineImpl::GetUser() {
161
 
  return user_jid_;
162
 
}
163
 
 
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
 
 
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
 
 
182
 
const std::string& XmppEngineImpl::GetRequestedResource() {
183
 
  return requested_resource_;
184
 
}
185
 
 
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
 
 
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
 
 
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_.get()) {
228
 
    login_task_->IncomingStanza(NULL, false);
229
 
    if (login_task_->IsDone())
230
 
      login_task_.reset();
231
 
  }
232
 
 
233
 
  return XMPP_RETURN_OK;
234
 
}
235
 
 
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_.get()) {
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
 
 
253
 
XmppReturnStatus XmppEngineImpl::SendRaw(const std::string& text) {
254
 
  if (state_ == STATE_CLOSED || login_task_.get())
255
 
    return XMPP_RETURN_BADSTATE;
256
 
 
257
 
  EnterExit ee(this);
258
 
 
259
 
  (*output_) << text;
260
 
 
261
 
  return XMPP_RETURN_OK;
262
 
}
263
 
 
264
 
std::string XmppEngineImpl::NextId() {
265
 
  std::stringstream ss;
266
 
  ss << next_id_++;
267
 
  return ss.str();
268
 
}
269
 
 
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
 
 
281
 
void XmppEngineImpl::IncomingStart(const XmlElement* start) {
282
 
  if (HasError() || raised_reset_)
283
 
    return;
284
 
 
285
 
  if (login_task_.get()) {
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
 
 
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_.get()) {
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
 
 
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
 
 
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
 
 
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
 
 
372
 
std::string XmppEngineImpl::ChooseBestSaslMechanism(
373
 
    const std::vector<std::string>& mechanisms, bool encrypted) {
374
 
  return sasl_handler_->ChooseBestSaslMechanism(mechanisms, encrypted);
375
 
}
376
 
 
377
 
SaslMechanism* XmppEngineImpl::GetSaslMechanism(const std::string& name) {
378
 
  return sasl_handler_->CreateSaslMechanism(name);
379
 
}
380
 
 
381
 
void XmppEngineImpl::SignalBound(const Jid& fullJid) {
382
 
  if (state_ == STATE_OPENING) {
383
 
    bound_jid_ = fullJid;
384
 
    state_ = STATE_OPEN;
385
 
  }
386
 
}
387
 
 
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
 
 
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
 
 
403
 
bool XmppEngineImpl::HasError() {
404
 
  return error_code_ != ERROR_NONE;
405
 
}
406
 
 
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
 
 
419
 
XmppEngineImpl::EnterExit::EnterExit(XmppEngineImpl* engine)
420
 
    : engine_(engine),
421
 
  state_(engine->state_),
422
 
  error_(engine->error_code_) {
423
 
  engine->engine_entered_ += 1;
424
 
}
425
 
 
426
 
XmppEngineImpl::EnterExit::~EnterExit()  {
427
 
 XmppEngineImpl* engine = engine_;
428
 
 
429
 
 engine->engine_entered_ -= 1;
430
 
 
431
 
 bool closing = (engine->state_ != state_ &&
432
 
       engine->state_ == STATE_CLOSED);
433
 
 bool flushing = closing || (engine->engine_entered_ == 0);
434
 
 
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("");
440
 
 
441
 
   if (closing) {
442
 
     engine->output_handler_->CloseConnection();
443
 
     engine->output_handler_ = 0;
444
 
   }
445
 
 }
446
 
 
447
 
 if (engine->engine_entered_)
448
 
   return;
449
 
 
450
 
 if (engine->raised_reset_) {
451
 
   engine->stanza_parser_.Reset();
452
 
   engine->raised_reset_ = false;
453
 
 }
454
 
 
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
460
 
   // after this line.
461
 
 }
462
 
}
463
 
 
464
 
}  // namespace buzz