1 #include <utils/timed_events.hpp>
2 #include <utils/scopeguard.hpp>
3 #include <utils/tolower.hpp>
4 #include <logger/logger.hpp>
5 #include <utils/uuid.hpp>
6
7 #include <xmpp/xmpp_component.hpp>
8 #include <config/config.hpp>
9 #include <utils/system.hpp>
10 #include <utils/time.hpp>
11 #include <xmpp/auth.hpp>
12 #include <xmpp/jid.hpp>
13
14 #include <stdexcept>
15 #include <iostream>
16 #include <set>
17
18 #include <cstdlib>
19 #include <set>
20
21 #include <biboumi.h>
22 #ifdef SYSTEMD_FOUND
23 # include <systemd/sd-daemon.h>
24 #endif
25
26 using namespace std::string_literals;
27
28 static std::set<std::string> kickable_errors{
29 "gone",
30 "internal-server-error",
31 "item-not-found",
32 "jid-malformed",
33 "recipient-unavailable",
34 "redirect",
35 "remote-server-not-found",
36 "remote-server-timeout",
37 "service-unavailable",
38 "malformed-error"
39 };
40
XmppComponent(std::shared_ptr<Poller> & poller,std::string hostname,std::string secret)41 XmppComponent::XmppComponent(std::shared_ptr<Poller>& poller, std::string hostname, std::string secret):
42 TCPClientSocketHandler(poller),
43 ever_auth(false),
44 first_connection_try(true),
45 secret(std::move(secret)),
46 authenticated(false),
47 doc_open(false),
48 served_hostname(std::move(hostname)),
49 stanza_handlers{},
50 adhoc_commands_handler(*this)
51 {
52 this->parser.add_stream_open_callback(std::bind(&XmppComponent::on_remote_stream_open, this,
53 std::placeholders::_1));
54 this->parser.add_stanza_callback(std::bind(&XmppComponent::on_stanza, this,
55 std::placeholders::_1));
56 this->parser.add_stream_close_callback(std::bind(&XmppComponent::on_remote_stream_close, this,
57 std::placeholders::_1));
58 this->stanza_handlers.emplace("handshake",
59 std::bind(&XmppComponent::handle_handshake, this,std::placeholders::_1));
60 this->stanza_handlers.emplace("error",
61 std::bind(&XmppComponent::handle_error, this,std::placeholders::_1));
62 }
63
start()64 void XmppComponent::start()
65 {
66 this->connect(Config::get("xmpp_server_ip", "127.0.0.1"), Config::get("port", "5347"), false);
67 }
68
is_document_open() const69 bool XmppComponent::is_document_open() const
70 {
71 return this->doc_open;
72 }
73
send_stanza(const Stanza & stanza)74 void XmppComponent::send_stanza(const Stanza& stanza)
75 {
76 std::string str = stanza.to_string();
77 log_debug("XMPP SENDING: ", str);
78 this->send_data(std::move(str));
79 }
80
on_connection_failed(const std::string & reason)81 void XmppComponent::on_connection_failed(const std::string& reason)
82 {
83 this->first_connection_try = false;
84 log_error("Failed to connect to the XMPP server: ", reason);
85 #ifdef SYSTEMD_FOUND
86 sd_notifyf(0, "STATUS=Failed to connect to the XMPP server: %s", reason.data());
87 #endif
88 }
89
on_connected()90 void XmppComponent::on_connected()
91 {
92 log_info("connected to XMPP server");
93 this->first_connection_try = true;
94 auto data = "<stream:stream to='" + this->served_hostname + \
95 "' xmlns:stream='http://etherx.jabber.org/streams' xmlns='" COMPONENT_NS "'>";
96 log_debug("XMPP SENDING: ", data);
97 this->send_data(std::move(data));
98 this->doc_open = true;
99 // We may have some pending data to send: this happens when we try to send
100 // some data before we are actually connected. We send that data right now, if any
101 this->send_pending_data();
102 }
103
on_connection_close(const std::string & error)104 void XmppComponent::on_connection_close(const std::string& error)
105 {
106 if (error.empty())
107 log_info("XMPP server closed connection");
108 else
109 log_info("XMPP server closed connection: ", error);
110 }
111
parse_in_buffer(const size_t size)112 void XmppComponent::parse_in_buffer(const size_t size)
113 {
114 // in_buf.size, or size, cannot be bigger than our read-size (4096) so it’s safe
115 // to cast.
116
117 if (!this->in_buf.empty())
118 { // This may happen if the parser could not allocate enough space for
119 // us. We try to feed it the data that was read into our in_buf
120 // instead. If this fails again we are in trouble.
121 this->parser.feed(this->in_buf.data(), static_cast<int>(this->in_buf.size()), false);
122 this->in_buf.clear();
123 }
124 else
125 { // Just tell the parser to parse the data that was placed into the
126 // buffer it provided to us with GetBuffer
127 this->parser.parse(static_cast<int>(size), false);
128 }
129 }
130
on_remote_stream_open(const XmlNode & node)131 void XmppComponent::on_remote_stream_open(const XmlNode& node)
132 {
133 log_debug("XMPP RECEIVING: ", node.to_string());
134 this->stream_id = node.get_tag("id");
135 if (this->stream_id.empty())
136 {
137 log_error("Error: no attribute 'id' found");
138 this->send_stream_error("bad-format", "missing 'id' attribute");
139 this->close_document();
140 return ;
141 }
142
143 // Try to authenticate
144 auto data = "<handshake xmlns='" COMPONENT_NS "'>" + get_handshake_digest(this->stream_id, this->secret) + "</handshake>";
145 log_debug("XMPP SENDING: ", data);
146 this->send_data(std::move(data));
147 }
148
on_remote_stream_close(const XmlNode & node)149 void XmppComponent::on_remote_stream_close(const XmlNode& node)
150 {
151 log_debug("XMPP RECEIVING: ", node.to_string());
152 this->doc_open = false;
153 }
154
reset()155 void XmppComponent::reset()
156 {
157 this->parser.reset();
158 }
159
on_stanza(const Stanza & stanza)160 void XmppComponent::on_stanza(const Stanza& stanza)
161 {
162 log_debug("XMPP RECEIVING: ", stanza.to_string());
163 std::function<void(const Stanza&)> handler;
164 try
165 {
166 handler = this->stanza_handlers.at(stanza.get_name());
167 }
168 catch (const std::out_of_range& exception)
169 {
170 log_warning("No handler for stanza of type ", stanza.get_name());
171 return;
172 }
173 handler(stanza);
174 }
175
send_stream_error(const std::string & name,const std::string & explanation)176 void XmppComponent::send_stream_error(const std::string& name, const std::string& explanation)
177 {
178 Stanza node("stream:error");
179 {
180 XmlSubNode error(node, name);
181 error["xmlns"] = STREAM_NS;
182 if (!explanation.empty())
183 error.set_inner(explanation);
184 }
185 this->send_stanza(node);
186 }
187
send_stanza_error(const std::string & kind,const std::string & to,const std::string & from,const std::string & id,const std::string & error_type,const std::string & defined_condition,const std::string & text,const bool fulljid)188 void XmppComponent::send_stanza_error(const std::string& kind, const std::string& to, const std::string& from,
189 const std::string& id, const std::string& error_type,
190 const std::string& defined_condition, const std::string& text,
191 const bool fulljid)
192 {
193 Stanza node(kind);
194 {
195 if (!to.empty())
196 node["to"] = to;
197 if (!from.empty())
198 {
199 if (fulljid)
200 node["from"] = from;
201 else
202 node["from"] = from + "@" + this->served_hostname;
203 }
204 if (!id.empty())
205 node["id"] = id;
206 node["type"] = "error";
207 {
208 XmlSubNode error(node, "error");
209 error["type"] = error_type;
210 {
211 XmlSubNode inner_error(error, defined_condition);
212 inner_error["xmlns"] = STANZA_NS;
213 }
214 if (!text.empty())
215 {
216 XmlSubNode text_node(error, "text");
217 text_node["xmlns"] = STANZA_NS;
218 text_node.set_inner(text);
219 }
220 }
221 }
222 this->send_stanza(node);
223 }
224
close_document()225 void XmppComponent::close_document()
226 {
227 log_debug("XMPP SENDING: </stream:stream>");
228 this->send_data("</stream:stream>");
229 this->doc_open = false;
230 }
231
handle_handshake(const Stanza &)232 void XmppComponent::handle_handshake(const Stanza&)
233 {
234 this->authenticated = true;
235 this->ever_auth = true;
236 log_info("Authenticated with the XMPP server");
237 #ifdef SYSTEMD_FOUND
238 sd_notify(0, "READY=1");
239 // Install an event that sends a keepalive to systemd. If biboumi crashes
240 // or hangs for too long, systemd will restart it.
241 uint64_t usec;
242 if (sd_watchdog_enabled(0, &usec) > 0)
243 {
244 TimedEventsManager::instance().add_event(TimedEvent(
245 std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::microseconds(usec / 2)),
246 []() { sd_notify(0, "WATCHDOG=1"); }));
247 }
248 #endif
249 this->after_handshake();
250 }
251
handle_error(const Stanza & stanza)252 void XmppComponent::handle_error(const Stanza& stanza)
253 {
254 const XmlNode* text = stanza.get_child("text", STREAMS_NS);
255 std::string error_message("Unspecified error");
256 if (text)
257 error_message = text->get_inner();
258 log_error("Stream error received from the XMPP server: ", error_message);
259 #ifdef SYSTEMD_FOUND
260 if (!this->ever_auth)
261 sd_notifyf(0, "STATUS=Failed to authenticate to the XMPP server: %s", error_message.data());
262 #endif
263 }
264
get_receive_buffer(const size_t size) const265 void* XmppComponent::get_receive_buffer(const size_t size) const
266 {
267 return this->parser.get_buffer(size);
268 }
269
send_message(const std::string & from,Xmpp::body && body,const std::string & to,const std::string & type,const bool fulljid,const bool nocopy,const bool muc_private)270 void XmppComponent::send_message(const std::string& from, Xmpp::body&& body, const std::string& to,
271 const std::string& type, const bool fulljid, const bool nocopy,
272 const bool muc_private)
273 {
274 Stanza message("message");
275 {
276 message["to"] = to;
277 if (fulljid)
278 message["from"] = from;
279 else
280 {
281 if (!from.empty())
282 message["from"] = from + "@" + this->served_hostname;
283 else
284 message["from"] = this->served_hostname;
285 }
286 if (!type.empty())
287 message["type"] = type;
288 XmlSubNode body_node(message, "body");
289 body_node.set_inner(std::get<0>(body));
290 if (std::get<1>(body))
291 {
292 XmlSubNode html(message, "html");
293 html["xmlns"] = XHTMLIM_NS;
294 // Pass the ownership of the pointer to this xmlnode
295 html.add_child(std::move(std::get<1>(body)));
296 }
297 if (nocopy)
298 {
299 XmlSubNode private_node(message, "private");
300 private_node["xmlns"] = "urn:xmpp:carbons:2";
301 XmlSubNode nocopy_node(message, "no-copy");
302 nocopy_node["xmlns"] = "urn:xmpp:hints";
303 }
304 if (muc_private)
305 {
306 XmlSubNode x(message, "x");
307 x["xmlns"] = MUC_USER_NS;
308 }
309 }
310 this->send_stanza(message);
311 }
312
send_user_join(const std::string & from,const std::string & nick,const std::string & realjid,const std::string & affiliation,const std::string & role,const std::string & to,const bool self)313 void XmppComponent::send_user_join(const std::string& from,
314 const std::string& nick,
315 const std::string& realjid,
316 const std::string& affiliation,
317 const std::string& role,
318 const std::string& to,
319 const bool self)
320 {
321 Stanza presence("presence");
322 {
323 presence["to"] = to;
324 presence["from"] = from + "@" + this->served_hostname + "/" + nick;
325
326 XmlSubNode x(presence, "x");
327 x["xmlns"] = MUC_USER_NS;
328
329 XmlSubNode item(x, "item");
330 if (!affiliation.empty())
331 item["affiliation"] = affiliation;
332 if (!role.empty())
333 item["role"] = role;
334 if (!realjid.empty())
335 {
336 const std::string preped_jid = jidprep(realjid);
337 if (!preped_jid.empty())
338 item["jid"] = preped_jid;
339 }
340
341 if (self)
342 {
343 XmlSubNode status_self(x, "status");
344 status_self["code"] = "110";
345 XmlSubNode status_nick_modified(x, "status");
346 status_nick_modified["code"] = "210";
347 XmlSubNode status_nonanonymous(x, "status");
348 status_nonanonymous["code"] = "100";
349 }
350 }
351 this->send_stanza(presence);
352 }
353
send_topic(const std::string & from,Xmpp::body && topic,const std::string & to,const std::string & who)354 void XmppComponent::send_topic(const std::string& from, Xmpp::body&& topic, const std::string& to, const std::string& who)
355 {
356 Stanza message("message");
357 {
358 message["to"] = to;
359 if (who.empty())
360 message["from"] = from + "@" + this->served_hostname;
361 else
362 message["from"] = from + "@" + this->served_hostname + "/" + who;
363 message["type"] = "groupchat";
364 XmlSubNode subject(message, "subject");
365 subject.set_inner(std::get<0>(topic));
366 }
367 this->send_stanza(message);
368 }
369
make_muc_message(const std::string & muc_name,const std::string & nick,Xmpp::body && xmpp_body,const std::string & jid_to,std::string uuid,std::string id)370 Stanza XmppComponent::make_muc_message(const std::string& muc_name, const std::string& nick, Xmpp::body&& xmpp_body, const std::string& jid_to, std::string uuid, std::string id)
371 {
372 Stanza message("message");
373 message["to"] = jid_to;
374 message["id"] = std::move(id);
375 if (!nick.empty())
376 message["from"] = muc_name + "@" + this->served_hostname + "/" + nick;
377 else // Message from the room itself
378 message["from"] = muc_name + "@" + this->served_hostname;
379 message["type"] = "groupchat";
380
381 {
382 XmlSubNode body(message, "body");
383 body.set_inner(std::get<0>(xmpp_body));
384 }
385
386 if (std::get<1>(xmpp_body))
387 {
388 XmlSubNode html(message, "html");
389 html["xmlns"] = XHTMLIM_NS;
390 // Pass the ownership of the pointer to this xmlnode
391 html.add_child(std::move(std::get<1>(xmpp_body)));
392 }
393
394 if (!uuid.empty())
395 {
396 XmlSubNode stanza_id(message, "stanza-id");
397 stanza_id["xmlns"] = STABLE_ID_NS;
398 stanza_id["by"] = muc_name + "@" + this->served_hostname;
399 stanza_id["id"] = std::move(uuid);
400 }
401
402 return message;
403 }
404
405 #ifdef USE_DATABASE
send_history_message(const std::string & muc_name,const std::string & nick,const std::string & body_txt,const std::string & jid_to,Database::time_point::rep timestamp)406 void XmppComponent::send_history_message(const std::string& muc_name, const std::string& nick, const std::string& body_txt, const std::string& jid_to, Database::time_point::rep timestamp)
407 {
408 Stanza message("message");
409 message["to"] = jid_to;
410 if (!nick.empty())
411 message["from"] = muc_name + "@" + this->served_hostname + "/" + nick;
412 else
413 message["from"] = muc_name + "@" + this->served_hostname;
414 message["type"] = "groupchat";
415
416 {
417 XmlSubNode body(message, "body");
418 body.set_inner(body_txt);
419 }
420 {
421 XmlSubNode delay(message, "delay");
422 delay["xmlns"] = DELAY_NS;
423 delay["from"] = muc_name + "@" + this->served_hostname;
424 delay["stamp"] = utils::to_string(timestamp);
425 }
426
427 this->send_stanza(message);
428 }
429 #endif
430
send_muc_leave(const std::string & muc_name,const std::string & nick,Xmpp::body && message,const std::string & jid_to,const bool self,const bool user_requested,const std::string & affiliation,const std::string & role)431 void XmppComponent::send_muc_leave(const std::string& muc_name, const std::string& nick, Xmpp::body&& message,
432 const std::string& jid_to, const bool self, const bool user_requested,
433 const std::string& affiliation, const std::string& role)
434 {
435 Stanza presence("presence");
436 {
437 presence["to"] = jid_to;
438 presence["from"] = muc_name + "@" + this->served_hostname + "/" + nick;
439 presence["type"] = "unavailable";
440 const std::string& message_str = std::get<0>(message);
441 XmlSubNode x(presence, "x");
442 x["xmlns"] = MUC_USER_NS;
443 if (self)
444 {
445 {
446 XmlSubNode status(x, "status");
447 status["code"] = "110";
448 }
449 if (!user_requested)
450 {
451 XmlSubNode status(x, "status");
452 status["code"] = "332";
453 }
454 }
455 XmlSubNode item(x, "item");
456 item["affiliation"] = affiliation;
457 item["role"] = role;
458 if (!message_str.empty())
459 {
460 XmlSubNode status(presence, "status");
461 status.set_inner(message_str);
462 }
463 }
464 this->send_stanza(presence);
465 }
466
send_nick_change(const std::string & muc_name,const std::string & old_nick,const std::string & new_nick,const std::string & affiliation,const std::string & role,const std::string & jid_to,const bool self)467 void XmppComponent::send_nick_change(const std::string& muc_name,
468 const std::string& old_nick,
469 const std::string& new_nick,
470 const std::string& affiliation,
471 const std::string& role,
472 const std::string& jid_to,
473 const bool self)
474 {
475 Stanza presence("presence");
476 {
477 presence["to"] = jid_to;
478 presence["from"] = muc_name + "@" + this->served_hostname + "/" + old_nick;
479 presence["type"] = "unavailable";
480 XmlSubNode x(presence, "x");
481 x["xmlns"] = MUC_USER_NS;
482 XmlSubNode item(x, "item");
483 item["nick"] = new_nick;
484 item["affiliation"] = affiliation;
485 item["role"] = role;
486 XmlSubNode status(x, "status");
487 status["code"] = "303";
488 if (self)
489 {
490 XmlSubNode status(x, "status");
491 status["code"] = "110";
492 }
493 }
494 this->send_stanza(presence);
495
496 this->send_user_join(muc_name, new_nick, "", affiliation, role, jid_to, self);
497 }
498
kick_user(const std::string & muc_name,const std::string & target,const std::string & txt,const std::string & author,const std::string & jid_to,const bool self)499 void XmppComponent::kick_user(const std::string& muc_name, const std::string& target, const std::string& txt,
500 const std::string& author, const std::string& jid_to, const bool self)
501 {
502 Stanza presence("presence");
503 {
504 presence["from"] = muc_name + "@" + this->served_hostname + "/" + target;
505 presence["to"] = jid_to;
506 presence["type"] = "unavailable";
507 XmlSubNode x(presence, "x");
508 x["xmlns"] = MUC_USER_NS;
509 XmlSubNode item(x, "item");
510 item["affiliation"] = "none";
511 item["role"] = "none";
512 XmlSubNode actor(item, "actor");
513 actor["nick"] = author;
514 actor["jid"] = author; // backward compatibility with old clients
515 XmlSubNode reason(item, "reason");
516 reason.set_inner(txt);
517 XmlSubNode status(x, "status");
518 status["code"] = "307";
519 if (self)
520 {
521 XmlSubNode status(x, "status");
522 status["code"] = "110";
523 }
524 }
525 this->send_stanza(presence);
526 }
527
send_presence_error(const std::string & muc_name,const std::string & nickname,const std::string & jid_to,const std::string & type,const std::string & condition,const std::string & error_code,const std::string & text)528 void XmppComponent::send_presence_error(const std::string& muc_name,
529 const std::string& nickname,
530 const std::string& jid_to,
531 const std::string& type,
532 const std::string& condition,
533 const std::string& error_code,
534 const std::string& text)
535 {
536 Stanza presence("presence");
537 {
538 presence["from"] = muc_name + "@" + this->served_hostname + "/" + nickname;
539 presence["to"] = jid_to;
540 presence["type"] = "error";
541 XmlSubNode x(presence, "x");
542 x["xmlns"] = MUC_NS;
543 XmlSubNode error(presence, "error");
544 error["by"] = muc_name + "@" + this->served_hostname;
545 error["type"] = type;
546 if (!text.empty())
547 {
548 XmlSubNode text_node(error, "text");
549 text_node["xmlns"] = STANZA_NS;
550 text_node.set_inner(text);
551 }
552 if (!error_code.empty())
553 error["code"] = error_code;
554 XmlSubNode subnode(error, condition);
555 subnode["xmlns"] = STANZA_NS;
556 }
557 this->send_stanza(presence);
558 }
559
send_affiliation_role_change(const std::string & muc_name,const std::string & target,const std::string & affiliation,const std::string & role,const std::string & jid_to)560 void XmppComponent::send_affiliation_role_change(const std::string& muc_name,
561 const std::string& target,
562 const std::string& affiliation,
563 const std::string& role,
564 const std::string& jid_to)
565 {
566 Stanza presence("presence");
567 {
568 presence["from"] = muc_name + "@" + this->served_hostname + "/" + target;
569 presence["to"] = jid_to;
570 XmlSubNode x(presence, "x");
571 x["xmlns"] = MUC_USER_NS;
572 XmlSubNode item(x, "item");
573 item["affiliation"] = affiliation;
574 item["role"] = role;
575 }
576 this->send_stanza(presence);
577 }
578
send_version(const std::string & id,const std::string & jid_to,const std::string & jid_from,const std::string & version)579 void XmppComponent::send_version(const std::string& id, const std::string& jid_to, const std::string& jid_from,
580 const std::string& version)
581 {
582 Stanza iq("iq");
583 iq["type"] = "result";
584 iq["id"] = id;
585 iq["to"] = jid_to;
586 iq["from"] = jid_from;
587 {
588 XmlSubNode query(iq, "query");
589 query["xmlns"] = VERSION_NS;
590 if (version.empty())
591 {
592 {
593 XmlSubNode name(query, "name");
594 name.set_inner("biboumi");
595 }
596 {
597 XmlSubNode version_node(query, "version");
598 version_node.set_inner(SOFTWARE_VERSION);
599 }
600 {
601 XmlSubNode os(query, "os");
602 os.set_inner(utils::get_system_name());
603 }
604 }
605 else
606 {
607 XmlSubNode name(query, "name");
608 name.set_inner(version);
609 }
610 }
611 this->send_stanza(iq);
612 }
613
send_adhoc_commands_list(const std::string & id,const std::string & requester_jid,const std::string & from_jid,const bool with_admin_only,const AdhocCommandsHandler & adhoc_handler)614 void XmppComponent::send_adhoc_commands_list(const std::string& id, const std::string& requester_jid,
615 const std::string& from_jid,
616 const bool with_admin_only, const AdhocCommandsHandler& adhoc_handler)
617 {
618 Stanza iq("iq");
619 {
620 iq["type"] = "result";
621 iq["id"] = id;
622 iq["to"] = requester_jid;
623 iq["from"] = from_jid;
624 XmlSubNode query(iq, "query");
625 query["xmlns"] = DISCO_ITEMS_NS;
626 query["node"] = ADHOC_NS;
627 for (const auto &kv: adhoc_handler.get_commands())
628 {
629 if (kv.second.is_admin_only() && !with_admin_only)
630 continue;
631 XmlSubNode item(query, "item");
632 item["jid"] = from_jid;
633 item["node"] = kv.first;
634 item["name"] = kv.second.name;
635 }
636 }
637 this->send_stanza(iq);
638 }
639
send_iq_version_request(const std::string & from,const std::string & jid_to)640 void XmppComponent::send_iq_version_request(const std::string& from,
641 const std::string& jid_to)
642 {
643 Stanza iq("iq");
644 {
645 iq["type"] = "get";
646 iq["id"] = "version_" + XmppComponent::next_id();
647 iq["from"] = from + "@" + this->served_hostname;
648 iq["to"] = jid_to;
649 XmlSubNode query(iq, "query");
650 query["xmlns"] = VERSION_NS;
651 }
652 this->send_stanza(iq);
653 }
654
send_iq_result_full_jid(const std::string & id,const std::string & to_jid,const std::string & from_full_jid,std::unique_ptr<XmlNode> inner)655 void XmppComponent::send_iq_result_full_jid(const std::string& id, const std::string& to_jid, const std::string& from_full_jid, std::unique_ptr<XmlNode> inner)
656 {
657 Stanza iq("iq");
658 iq["from"] = from_full_jid;
659 iq["to"] = to_jid;
660 iq["id"] = id;
661 iq["type"] = "result";
662 if (inner)
663 iq.add_child(std::move(inner));
664 this->send_stanza(iq);
665 }
666
send_iq_result(const std::string & id,const std::string & to_jid,const std::string & from_local_part)667 void XmppComponent::send_iq_result(const std::string& id, const std::string& to_jid, const std::string& from_local_part)
668 {
669 Stanza iq("iq");
670 if (!from_local_part.empty())
671 iq["from"] = from_local_part + "@" + this->served_hostname;
672 else
673 iq["from"] = this->served_hostname;
674 iq["to"] = to_jid;
675 iq["id"] = id;
676 iq["type"] = "result";
677 this->send_stanza(iq);
678 }
679
next_id()680 std::string XmppComponent::next_id()
681 {
682 return utils::gen_uuid();
683 }
684