OLD | NEW |
| (Empty) |
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 | |
26 XmppEngine* XmppEngine::Create() { | |
27 return new XmppEngineImpl(); | |
28 } | |
29 | |
30 | |
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 | |
60 XmppEngineImpl::~XmppEngineImpl() { | |
61 DeleteIqCookies(); | |
62 } | |
63 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
130 TlsOptions XmppEngineImpl::GetTls() { | |
131 return tls_option_; | |
132 } | |
133 | |
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 | |
143 const Jid& XmppEngineImpl::GetUser() { | |
144 return user_jid_; | |
145 } | |
146 | |
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 | |
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 | |
165 const std::string& XmppEngineImpl::GetRequestedResource() { | |
166 return requested_resource_; | |
167 } | |
168 | |
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 | |
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 | |
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 | |
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 | |
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 | |
247 std::string XmppEngineImpl::NextId() { | |
248 std::stringstream ss; | |
249 ss << next_id_++; | |
250 return ss.str(); | |
251 } | |
252 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
355 std::string XmppEngineImpl::ChooseBestSaslMechanism( | |
356 const std::vector<std::string>& mechanisms, bool encrypted) { | |
357 return sasl_handler_->ChooseBestSaslMechanism(mechanisms, encrypted); | |
358 } | |
359 | |
360 SaslMechanism* XmppEngineImpl::GetSaslMechanism(const std::string& name) { | |
361 return sasl_handler_->CreateSaslMechanism(name); | |
362 } | |
363 | |
364 void XmppEngineImpl::SignalBound(const Jid& fullJid) { | |
365 if (state_ == STATE_OPENING) { | |
366 bound_jid_ = fullJid; | |
367 state_ = STATE_OPEN; | |
368 } | |
369 } | |
370 | |
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 | |
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 | |
386 bool XmppEngineImpl::HasError() { | |
387 return error_code_ != ERROR_NONE; | |
388 } | |
389 | |
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 | |
402 XmppEngineImpl::EnterExit::EnterExit(XmppEngineImpl* engine) | |
403 : engine_(engine), | |
404 state_(engine->state_) { | |
405 engine->engine_entered_ += 1; | |
406 } | |
407 | |
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 | |
OLD | NEW |