summaryrefslogtreecommitdiff
path: root/src/xmpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/xmpp')
-rw-r--r--src/xmpp/adhoc_commands_handler.cpp5
-rw-r--r--src/xmpp/biboumi_adhoc_commands.cpp104
-rw-r--r--src/xmpp/biboumi_component.cpp139
-rw-r--r--src/xmpp/biboumi_component.hpp3
-rw-r--r--src/xmpp/jid.cpp2
-rw-r--r--src/xmpp/xmpp_component.cpp22
-rw-r--r--src/xmpp/xmpp_component.hpp5
-rw-r--r--src/xmpp/xmpp_parser.cpp2
8 files changed, 211 insertions, 71 deletions
diff --git a/src/xmpp/adhoc_commands_handler.cpp b/src/xmpp/adhoc_commands_handler.cpp
index bc4c108..ff4c1e5 100644
--- a/src/xmpp/adhoc_commands_handler.cpp
+++ b/src/xmpp/adhoc_commands_handler.cpp
@@ -80,7 +80,10 @@ XmlNode AdhocCommandsHandler::handle_request(const std::string& executor_jid, co
{
command_node["status"] = "executing";
XmlSubNode actions(command_node, "actions");
- XmlSubNode next(actions, "next");
+ if (session.remaining_steps() == 1)
+ XmlSubNode next(actions, "complete");
+ else
+ XmlSubNode next(actions, "next");
}
}
else if (session_it != this->sessions.end() && action == "cancel")
diff --git a/src/xmpp/biboumi_adhoc_commands.cpp b/src/xmpp/biboumi_adhoc_commands.cpp
index 3bd2e5a..113943c 100644
--- a/src/xmpp/biboumi_adhoc_commands.cpp
+++ b/src/xmpp/biboumi_adhoc_commands.cpp
@@ -15,10 +15,17 @@
#ifdef USE_DATABASE
#include <database/database.hpp>
#include <database/save.hpp>
+
+static void set_desc(XmlSubNode& field, const char* text)
+{
+ XmlSubNode desc(field, "desc");
+ desc.set_inner(text);
+}
+
#endif
#ifndef HAS_PUT_TIME
-#include <ctime>
+# include <time.h>
#endif
using namespace std::string_literals;
@@ -116,6 +123,7 @@ void ConfigureGlobalStep1(XmppComponent&, AdhocSession& session, XmlNode& comman
auto options = Database::get_global_options(owner.bare());
+ command_node.delete_all_children();
XmlSubNode x(command_node, "jabber:x:data:x");
x["type"] = "form";
XmlSubNode title(x, "title");
@@ -128,7 +136,7 @@ void ConfigureGlobalStep1(XmppComponent&, AdhocSession& session, XmlNode& comman
max_histo_length["var"] = "max_history_length";
max_histo_length["type"] = "text-single";
max_histo_length["label"] = "Max history length";
- max_histo_length["desc"] = "The maximum number of lines in the history that the server sends when joining a channel";
+ set_desc(max_histo_length, "The maximum number of lines in the history that the server sends when joining a channel");
{
XmlSubNode value(max_histo_length, "value");
value.set_inner(std::to_string(options.col<Database::MaxHistoryLength>()));
@@ -140,7 +148,7 @@ void ConfigureGlobalStep1(XmppComponent&, AdhocSession& session, XmlNode& comman
record_history["var"] = "record_history";
record_history["type"] = "boolean";
record_history["label"] = "Record history";
- record_history["desc"] = "Whether to save the messages into the database, or not";
+ set_desc(record_history, "Whether to save the messages into the database, or not");
{
XmlSubNode value(record_history, "value");
value.set_name("value");
@@ -156,7 +164,7 @@ void ConfigureGlobalStep1(XmppComponent&, AdhocSession& session, XmlNode& comman
persistent["var"] = "persistent";
persistent["type"] = "boolean";
persistent["label"] = "Make all channels persistent";
- persistent["desc"] = "If true, all channels will be persistent";
+ set_desc(persistent, "If true, all channels will be persistent");
{
XmlSubNode value(persistent, "value");
value.set_name("value");
@@ -184,7 +192,13 @@ void ConfigureGlobalStep2(XmppComponent& xmpp_component, AdhocSession& session,
if (field->get_tag("var") == "max_history_length" &&
value && !value->get_inner().empty())
- options.col<Database::MaxHistoryLength>() = atoi(value->get_inner().data());
+ {
+ try {
+ options.col<Database::MaxHistoryLength>() = std::stol(value->get_inner().data());
+ } catch (const std::logic_error&) {
+ options.col<Database::MaxHistoryLength>() = 20;
+ }
+ }
else if (field->get_tag("var") == "record_history" &&
value && !value->get_inner().empty())
{
@@ -223,6 +237,7 @@ void ConfigureIrcServerStep1(XmppComponent&, AdhocSession& session, XmlNode& com
server_domain);
auto commands = Database::get_after_connection_commands(options);
+ command_node.delete_all_children();
XmlSubNode x(command_node, "jabber:x:data:x");
x["type"] = "form";
XmlSubNode title(x, "title");
@@ -236,7 +251,7 @@ void ConfigureIrcServerStep1(XmppComponent&, AdhocSession& session, XmlNode& com
field["var"] = "address";
field["type"] = "text-single";
field["label"] = "Address";
- field["desc"] = "The address (hostname or IP) to connect to.";
+ set_desc(field, "The address (hostname or IP) to connect to.");
XmlSubNode value(field, "value");
if (options.col<Database::Address>().empty())
value.set_inner(server_domain);
@@ -249,7 +264,7 @@ void ConfigureIrcServerStep1(XmppComponent&, AdhocSession& session, XmlNode& com
ports["var"] = "ports";
ports["type"] = "text-multi";
ports["label"] = "Ports";
- ports["desc"] = "List of ports to try, without TLS. Defaults: 6667.";
+ set_desc(ports, "List of ports to try, without TLS. Defaults: 6667.");
for (const auto& val: utils::split(options.col<Database::Ports>(), ';', false))
{
XmlSubNode ports_value(ports, "value");
@@ -263,7 +278,7 @@ void ConfigureIrcServerStep1(XmppComponent&, AdhocSession& session, XmlNode& com
tls_ports["var"] = "tls_ports";
tls_ports["type"] = "text-multi";
tls_ports["label"] = "TLS ports";
- tls_ports["desc"] = "List of ports to try, with TLS. Defaults: 6697, 6670.";
+ set_desc(tls_ports, "List of ports to try, with TLS. Defaults: 6697, 6670.");
for (const auto& val: utils::split(options.col<Database::TlsPorts>(), ';', false))
{
XmlSubNode tls_ports_value(tls_ports, "value");
@@ -276,7 +291,7 @@ void ConfigureIrcServerStep1(XmppComponent&, AdhocSession& session, XmlNode& com
verify_cert["var"] = "verify_cert";
verify_cert["type"] = "boolean";
verify_cert["label"] = "Verify certificate";
- verify_cert["desc"] = "Whether or not to abort the connection if the server’s TLS certificate is invalid";
+ set_desc(verify_cert, "Whether or not to abort the connection if the server’s TLS certificate is invalid");
XmlSubNode verify_cert_value(verify_cert, "value");
if (options.col<Database::VerifyCert>())
verify_cert_value.set_inner("true");
@@ -302,7 +317,7 @@ void ConfigureIrcServerStep1(XmppComponent&, AdhocSession& session, XmlNode& com
field["var"] = "nick";
field["type"] = "text-single";
field["label"] = "Nickname";
- field["desc"] = "If set, will override the nickname provided in the initial presence sent to join the first server channel";
+ set_desc(field, "If set, will override the nickname provided in the initial presence sent to join the first server channel");
if (!options.col<Database::Nick>().empty())
{
XmlSubNode value(field, "value");
@@ -315,7 +330,7 @@ void ConfigureIrcServerStep1(XmppComponent&, AdhocSession& session, XmlNode& com
pass["var"] = "pass";
pass["type"] = "text-private";
pass["label"] = "Server password";
- pass["desc"] = "Will be used in a PASS command when connecting";
+ set_desc(pass, "Will be used in a PASS command when connecting");
if (!options.col<Database::Pass>().empty())
{
XmlSubNode pass_value(pass, "value");
@@ -327,7 +342,7 @@ void ConfigureIrcServerStep1(XmppComponent&, AdhocSession& session, XmlNode& com
XmlSubNode after_cnt_cmd(x, "field");
after_cnt_cmd["var"] = "after_connect_commands";
after_cnt_cmd["type"] = "text-multi";
- after_cnt_cmd["desc"] = "Custom IRC commands sent after the connection is established with the server.";
+ set_desc(after_cnt_cmd, "Custom IRC commands sent after the connection is established with the server.");
after_cnt_cmd["label"] = "After-connection IRC commands";
for (const auto& command: commands)
{
@@ -364,10 +379,28 @@ void ConfigureIrcServerStep1(XmppComponent&, AdhocSession& session, XmlNode& com
}
{
+ XmlSubNode throttle_limit(x, "field");
+ throttle_limit["var"] = "throttle_limit";
+ throttle_limit["type"] = "text-single";
+ throttle_limit["label"] = "Throttle limit";
+ XmlSubNode value(throttle_limit, "value");
+ value.set_inner(std::to_string(options.col<Database::ThrottleLimit>()));
+ }
+
+ {
+ XmlSubNode max_history_length(x, "field");
+ max_history_length["var"] = "max_history_length";
+ max_history_length["type"] = "text-single";
+ max_history_length["label"] = "Throttle limit";
+ XmlSubNode value(max_history_length, "value");
+ value.set_inner(std::to_string(options.col<Database::MaxHistoryLength>()));
+ }
+
+ {
XmlSubNode encoding_out(x, "field");
encoding_out["var"] = "encoding_out";
encoding_out["type"] = "text-single";
- encoding_out["desc"] = "The encoding used when sending messages to the IRC server.";
+ set_desc(encoding_out, "The encoding used when sending messages to the IRC server.");
encoding_out["label"] = "Out encoding";
if (!options.col<Database::EncodingOut>().empty())
{
@@ -380,7 +413,7 @@ void ConfigureIrcServerStep1(XmppComponent&, AdhocSession& session, XmlNode& com
XmlSubNode encoding_in(x, "field");
encoding_in["var"] = "encoding_in";
encoding_in["type"] = "text-single";
- encoding_in["desc"] = "The encoding used to decode message received from the IRC server.";
+ set_desc(encoding_in, "The encoding used to decode message received from the IRC server.");
encoding_in["label"] = "In encoding";
if (!options.col<Database::EncodingIn>().empty())
{
@@ -390,8 +423,10 @@ void ConfigureIrcServerStep1(XmppComponent&, AdhocSession& session, XmlNode& com
}
}
-void ConfigureIrcServerStep2(XmppComponent&, AdhocSession& session, XmlNode& command_node)
+void ConfigureIrcServerStep2(XmppComponent& xmpp_component, AdhocSession& session, XmlNode& command_node)
{
+ auto& biboumi_component = dynamic_cast<BiboumiComponent&>(xmpp_component);
+
const XmlNode* x = command_node.get_child("x", "jabber:x:data");
if (x)
{
@@ -472,6 +507,31 @@ void ConfigureIrcServerStep2(XmppComponent&, AdhocSession& session, XmlNode& com
else if (field->get_tag("var") == "realname" && value)
options.col<Database::Realname>() = value->get_inner();
+ else if (field->get_tag("var") == "throttle_limit" && value)
+ {
+ try {
+ options.col<Database::ThrottleLimit>() = std::stol(value->get_inner());
+ } catch (const std::logic_error&) {
+ options.col<Database::ThrottleLimit>() = 10;
+ }
+ Bridge* bridge = biboumi_component.find_user_bridge(session.get_owner_jid());
+ if (bridge)
+ {
+ IrcClient* client = bridge->find_irc_client(server_domain);
+ if (client)
+ client->set_throttle_limit(options.col<Database::ThrottleLimit>());
+ }
+ }
+
+ else if (field->get_tag("var") == "max_history_length" && value)
+ {
+ try {
+ options.col<Database::MaxHistoryLength>() = std::stol(value->get_inner());
+ } catch (const std::logic_error&) {
+ options.col<Database::MaxHistoryLength>() = 20;
+ }
+ }
+
else if (field->get_tag("var") == "encoding_out" && value)
options.col<Database::EncodingOut>() = value->get_inner();
@@ -509,6 +569,7 @@ void insert_irc_channel_configuration_form(XmlNode& node, const Jid& requester,
auto options = Database::get_irc_channel_options_with_server_default(requester.local + "@" + requester.domain,
iid.get_server(), iid.get_local());
+ node.delete_all_children();
XmlSubNode x(node, "jabber:x:data:x");
x["type"] = "form";
XmlSubNode title(x, "title");
@@ -521,7 +582,7 @@ void insert_irc_channel_configuration_form(XmlNode& node, const Jid& requester,
record_history["var"] = "record_history";
record_history["type"] = "list-single";
record_history["label"] = "Record history for this channel";
- record_history["desc"] = "If unset, the value is the one configured globally";
+ set_desc(record_history, "If unset, the value is the one configured globally");
{
// Value selected by default
XmlSubNode value(record_history, "value");
@@ -541,7 +602,7 @@ void insert_irc_channel_configuration_form(XmlNode& node, const Jid& requester,
XmlSubNode encoding_out(x, "field");
encoding_out["var"] = "encoding_out";
encoding_out["type"] = "text-single";
- encoding_out["desc"] = "The encoding used when sending messages to the IRC server. Defaults to the server's “out encoding” if unset for the channel";
+ set_desc(encoding_out, "The encoding used when sending messages to the IRC server. Defaults to the server's “out encoding” if unset for the channel");
encoding_out["label"] = "Out encoding";
if (!options.col<Database::EncodingOut>().empty())
{
@@ -554,7 +615,7 @@ void insert_irc_channel_configuration_form(XmlNode& node, const Jid& requester,
XmlSubNode encoding_in(x, "field");
encoding_in["var"] = "encoding_in";
encoding_in["type"] = "text-single";
- encoding_in["desc"] = "The encoding used to decode message received from the IRC server. Defaults to the server's “in encoding” if unset for the channel";
+ set_desc(encoding_in, "The encoding used to decode message received from the IRC server. Defaults to the server's “in encoding” if unset for the channel");
encoding_in["label"] = "In encoding";
if (!options.col<Database::EncodingIn>().empty())
{
@@ -567,7 +628,7 @@ void insert_irc_channel_configuration_form(XmlNode& node, const Jid& requester,
XmlSubNode persistent(x, "field");
persistent["var"] = "persistent";
persistent["type"] = "boolean";
- persistent["desc"] = "If set to true, when all XMPP clients have left this channel, biboumi will stay idle in it, without sending a PART command.";
+ set_desc(persistent, "If set to true, when all XMPP clients have left this channel, biboumi will stay idle in it, without sending a PART command.");
persistent["label"] = "Persistent";
{
XmlSubNode value(persistent, "value");
@@ -847,12 +908,13 @@ void GetIrcConnectionInfoStep1(XmppComponent& component, AdhocSession& session,
if (irc->is_using_tls())
ss << " (using TLS)";
const std::time_t now_c = std::chrono::system_clock::to_time_t(irc->connection_date);
+ struct tm tm;
#ifdef HAS_PUT_TIME
- ss << " since " << std::put_time(std::localtime(&now_c), "%F %T");
+ ss << " since " << std::put_time(localtime_r(&now_c, &tm), "%F %T");
#else
constexpr std::size_t timestamp_size{10 + 1 + 8 + 1};
char buf[timestamp_size] = {};
- const auto res = std::strftime(buf, timestamp_size, "%F %T", std::localtime(&now_c));
+ const auto res = std::strftime(buf, timestamp_size, "%F %T", localtime(&now_c, &tm));
if (res > 0)
ss << " since " << buf;
#endif
diff --git a/src/xmpp/biboumi_component.cpp b/src/xmpp/biboumi_component.cpp
index be34873..f49b3b6 100644
--- a/src/xmpp/biboumi_component.cpp
+++ b/src/xmpp/biboumi_component.cpp
@@ -102,8 +102,8 @@ void BiboumiComponent::shutdown()
void BiboumiComponent::clean()
{
- auto it = this->bridges.begin();
- while (it != this->bridges.end())
+ auto it = std::begin(this->bridges);
+ while (it != std::end(this->bridges))
{
it->second->clean();
if (it->second->active_clients() == 0)
@@ -158,35 +158,51 @@ void BiboumiComponent::handle_presence(const Stanza& stanza)
{
const std::string own_nick = bridge->get_own_nick(iid);
const XmlNode* x = stanza.get_child("x", MUC_NS);
- const XmlNode* password = x ? x->get_child("password", MUC_NS): nullptr;
- const XmlNode* history = x ? x->get_child("history", MUC_NS): nullptr;
- HistoryLimit history_limit;
- if (history)
+ const IrcClient* irc = bridge->find_irc_client(iid.get_server());
+ // if there is no <x/>, this is a presence status update, we don’t care about those
+ if (x)
{
- const auto seconds = history->get_tag("seconds");
- if (!seconds.empty())
+ const XmlNode* password = x->get_child("password", MUC_NS);
+ const XmlNode* history = x->get_child("history", MUC_NS);
+ HistoryLimit history_limit;
+ if (history)
{
- const auto now = std::chrono::system_clock::now();
- std::time_t timestamp = std::chrono::system_clock::to_time_t(now);
- int int_seconds = std::atoi(seconds.data());
- timestamp -= int_seconds;
- history_limit.since = utils::to_string(timestamp);
+ const auto seconds = history->get_tag("seconds");
+ if (!seconds.empty())
+ {
+ const auto now = std::chrono::system_clock::now();
+ std::time_t timestamp = std::chrono::system_clock::to_time_t(now);
+ int int_seconds = std::atoi(seconds.data());
+ timestamp -= int_seconds;
+ history_limit.since = utils::to_string(timestamp);
+ }
+ const auto since = history->get_tag("since");
+ if (!since.empty())
+ history_limit.since = since;
+ const auto maxstanzas = history->get_tag("maxstanzas");
+ if (!maxstanzas.empty())
+ history_limit.stanzas = std::atoi(maxstanzas.data());
+ // Ignore any other value, because this is too complex to implement,
+ // so I won’t do it.
+ if (history->get_tag("maxchars") == "0")
+ history_limit.stanzas = 0;
+ }
+ bridge->join_irc_channel(iid, to.resource, password ? password->get_inner(): "",
+ from.resource, history_limit);
+ }
+ else
+ {
+ if (irc)
+ {
+ const auto chan = irc->find_channel(iid.get_local());
+ if (chan && chan->joined)
+ bridge->send_irc_nick_change(iid, to.resource, from.resource);
+ else
+ { // send an error if we are not joined yet, instead of treating it as a join
+ this->send_stanza_error("presence", from_str, to_str, id, "modify", "not-acceptable", "You are not joined to this MUC.");
+ }
}
- const auto since = history->get_tag("since");
- if (!since.empty())
- history_limit.since = since;
- const auto maxstanzas = history->get_tag("maxstanzas");
- if (!maxstanzas.empty())
- history_limit.stanzas = std::atoi(maxstanzas.data());
- // Ignore any other value, because this is too complex to implement,
- // so I won’t do it.
- if (history->get_tag("maxchars") == "0")
- history_limit.stanzas = 0;
}
- bridge->join_irc_channel(iid, to.resource, password ? password->get_inner(): "",
- from.resource, history_limit, x != nullptr);
- if (!own_nick.empty() && own_nick != to.resource)
- bridge->send_irc_nick_change(iid, to.resource, from.resource);
}
else if (type == "unavailable")
{
@@ -273,9 +289,10 @@ void BiboumiComponent::handle_message(const Stanza& stanza)
std::string error_type("cancel");
std::string error_name("internal-server-error");
- utils::ScopeGuard stanza_error([this, &from_str, &to_str, &id, &error_type, &error_name](){
+ std::string error_text{};
+ utils::ScopeGuard stanza_error([this, &from_str, &to_str, &id, &error_type, &error_name, &error_text](){
this->send_stanza_error("message", from_str, to_str, id,
- error_type, error_name, "");
+ error_type, error_name, error_text);
});
const XmlNode* body = stanza.get_child("body", COMPONENT_NS);
@@ -284,7 +301,34 @@ void BiboumiComponent::handle_message(const Stanza& stanza)
{
if (body && !body->get_inner().empty())
{
- bridge->send_channel_message(iid, body->get_inner(), id);
+ if (bridge->is_resource_in_chan(iid.to_tuple(), from.resource))
+ {
+ // Extract some XML nodes that we must include in the
+ // reflection (if any), because XMPP says so
+ std::vector<XmlNode> nodes_to_reflect;
+ const XmlNode* origin_id = stanza.get_child("origin-id", STABLE_ID_NS);
+ if (origin_id)
+ nodes_to_reflect.push_back(*origin_id);
+ const auto own_address = std::to_string(iid) + '@' + this->served_hostname;
+ for (const XmlNode* stanza_id: stanza.get_children("stanza-id", STABLE_ID_NS))
+ {
+ // Stanza ID generating entities, which encounter a
+ // <stanza-id/> element where the 'by' attribute matches
+ // the 'by' attribute they would otherwise set, MUST
+ // delete that element even if they are not adding their
+ // own stanza ID.
+ if (stanza_id->get_tag("by") != own_address)
+ nodes_to_reflect.push_back(*stanza_id);
+ }
+ bridge->send_channel_message(iid, body->get_inner(), id, std::move(nodes_to_reflect));
+ }
+ else
+ {
+ error_type = "modify";
+ error_name = "not-acceptable";
+ error_text = "You are not a participant in this room.";
+ return;
+ }
}
const XmlNode* subject = stanza.get_child("subject", COMPONENT_NS);
if (subject)
@@ -350,7 +394,6 @@ void BiboumiComponent::handle_message(const Stanza& stanza)
this->send_invitation_from_fulljid(std::to_string(iid), invite_to, from_str);
}
}
-
}
} catch (const IRCNotConnected& ex)
{
@@ -514,7 +557,11 @@ void BiboumiComponent::handle_iq(const Stanza& stanza)
{
if (node.empty())
{
- this->send_irc_channel_disco_info(id, from, to_str);
+ const IrcClient* irc_client = bridge->find_irc_client(iid.get_server());
+ const IrcChannel* irc_channel{};
+ if (irc_client)
+ irc_channel = irc_client->find_channel(iid.get_local());
+ this->send_irc_channel_disco_info(id, from, to_str, irc_channel);
stanza_error.disable();
}
else if (node == MUC_TRAFFIC_NS)
@@ -592,7 +639,6 @@ void BiboumiComponent::handle_iq(const Stanza& stanza)
const XmlNode* max = set_node->get_child("max", RSM_NS);
if (max)
rs_info.max = std::atoi(max->get_inner().data());
-
}
if (rs_info.max == -1)
rs_info.max = 100;
@@ -751,7 +797,7 @@ bool BiboumiComponent::handle_mam_request(const Stanza& stanza)
if (limit < 0 || limit > 100)
limit = 100;
auto result = Database::get_muc_logs(from.bare(), iid.get_local(), iid.get_server(),
- limit,
+ static_cast<std::size_t>(limit),
start, end,
reference_record_id, paging_order);
bool complete = std::get<bool>(result);
@@ -964,7 +1010,8 @@ void BiboumiComponent::send_irc_channel_muc_traffic_info(const std::string& id,
this->send_stanza(iq);
}
-void BiboumiComponent::send_irc_channel_disco_info(const std::string& id, const std::string& jid_to, const std::string& jid_from)
+void BiboumiComponent::send_irc_channel_disco_info(const std::string& id, const std::string& jid_to,
+ const std::string& jid_from, const IrcChannel* irc_channel)
{
Jid from(jid_from);
Iid iid(from.local, {});
@@ -980,11 +1027,31 @@ void BiboumiComponent::send_irc_channel_disco_info(const std::string& id, const
identity["category"] = "conference";
identity["type"] = "irc";
identity["name"] = ""s + iid.get_local() + " on " + iid.get_server();
- for (const char *ns: {DISCO_INFO_NS, MUC_NS, ADHOC_NS, PING_NS, MAM_NS, VERSION_NS, STABLE_MUC_ID_NS})
+ for (const char *ns: {DISCO_INFO_NS, MUC_NS, ADHOC_NS, PING_NS, MAM_NS, VERSION_NS, STABLE_MUC_ID_NS, SELF_PING_FLAG, "muc_nonanonymous", STABLE_ID_NS})
{
XmlSubNode feature(query, "feature");
feature["var"] = ns;
}
+
+ XmlSubNode x(query, "x");
+ x["xmlns"] = DATAFORM_NS;
+ x["type"] = "result";
+ {
+ XmlSubNode field(x, "field");
+ field["var"] = "FORM_TYPE";
+ field["type"] = "hidden";
+ XmlSubNode value(field, "value");
+ value.set_inner("http://jabber.org/protocol/muc#roominfo");
+ }
+
+ if (irc_channel && irc_channel->joined)
+ {
+ XmlSubNode field(x, "field");
+ field["var"] = "muc#roominfo_occupants";
+ field["label"] = "Number of occupants";
+ XmlSubNode value(field, "value");
+ value.set_inner(std::to_string(irc_channel->get_users().size()));
+ }
}
this->send_stanza(iq);
}
diff --git a/src/xmpp/biboumi_component.hpp b/src/xmpp/biboumi_component.hpp
index caf990e..f59ed9b 100644
--- a/src/xmpp/biboumi_component.hpp
+++ b/src/xmpp/biboumi_component.hpp
@@ -73,7 +73,8 @@ public:
* http://xmpp.org/extensions/xep-0045.html#impl-service-traffic
*/
void send_irc_channel_muc_traffic_info(const std::string& id, const std::string& jid_to, const std::string& jid_from);
- void send_irc_channel_disco_info(const std::string& id, const std::string& jid_to, const std::string& jid_from);
+ void send_irc_channel_disco_info(const std::string& id, const std::string& jid_to, const std::string& jid_from,
+ const IrcChannel* irc_channel);
/**
* Send a ping request
*/
diff --git a/src/xmpp/jid.cpp b/src/xmpp/jid.cpp
index 19d1b55..3c54fd4 100644
--- a/src/xmpp/jid.cpp
+++ b/src/xmpp/jid.cpp
@@ -106,7 +106,7 @@ std::string jidprep(const std::string& original)
--domain_end;
if (domain_end != domain && special_chars.count(domain[0]))
{
- std::memmove(domain, domain + 1, domain_end - domain + 1);
+ std::memmove(domain, domain + 1, static_cast<std::size_t>(domain_end - domain) + 1);
--domain_end;
}
// And if the final result is an empty string, return a dummy hostname
diff --git a/src/xmpp/xmpp_component.cpp b/src/xmpp/xmpp_component.cpp
index b3d925e..de9a7a6 100644
--- a/src/xmpp/xmpp_component.cpp
+++ b/src/xmpp/xmpp_component.cpp
@@ -298,8 +298,8 @@ void XmppComponent::send_message(const std::string& from, Xmpp::body&& body, con
{
XmlSubNode private_node(message, "private");
private_node["xmlns"] = "urn:xmpp:carbons:2";
- XmlSubNode nocopy(message, "no-copy");
- nocopy["xmlns"] = "urn:xmpp:hints";
+ XmlSubNode nocopy_node(message, "no-copy");
+ nocopy_node["xmlns"] = "urn:xmpp:hints";
}
if (muc_private)
{
@@ -340,8 +340,12 @@ void XmppComponent::send_user_join(const std::string& from,
if (self)
{
- XmlSubNode status(x, "status");
- status["code"] = "110";
+ XmlSubNode status_self(x, "status");
+ status_self["code"] = "110";
+ XmlSubNode status_nick_modified(x, "status");
+ status_nick_modified["code"] = "210";
+ XmlSubNode status_nonanonymous(x, "status");
+ status_nonanonymous["code"] = "100";
}
}
this->send_stanza(presence);
@@ -363,7 +367,7 @@ void XmppComponent::send_topic(const std::string& from, Xmpp::body&& topic, cons
this->send_stanza(message);
}
-void XmppComponent::send_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)
+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)
{
Stanza message("message");
message["to"] = jid_to;
@@ -395,7 +399,7 @@ void XmppComponent::send_muc_message(const std::string& muc_name, const std::str
stanza_id["id"] = std::move(uuid);
}
- this->send_stanza(message);
+ return message;
}
#ifdef USE_DATABASE
@@ -477,6 +481,8 @@ void XmppComponent::send_nick_change(const std::string& muc_name,
x["xmlns"] = MUC_USER_NS;
XmlSubNode item(x, "item");
item["nick"] = new_nick;
+ item["affiliation"] = affiliation;
+ item["role"] = role;
XmlSubNode status(x, "status");
status["code"] = "303";
if (self)
@@ -588,8 +594,8 @@ void XmppComponent::send_version(const std::string& id, const std::string& jid_t
name.set_inner("biboumi");
}
{
- XmlSubNode version(query, "version");
- version.set_inner(SOFTWARE_VERSION);
+ XmlSubNode version_node(query, "version");
+ version_node.set_inner(SOFTWARE_VERSION);
}
{
XmlSubNode os(query, "os");
diff --git a/src/xmpp/xmpp_component.hpp b/src/xmpp/xmpp_component.hpp
index e18da40..ee6b776 100644
--- a/src/xmpp/xmpp_component.hpp
+++ b/src/xmpp/xmpp_component.hpp
@@ -38,6 +38,7 @@
#define MUC_TRAFFIC_NS "http://jabber.org/protocol/muc#traffic"
#define STABLE_ID_NS "urn:xmpp:sid:0"
#define STABLE_MUC_ID_NS "http://jabber.org/protocol/muc#stable_id"
+#define SELF_PING_FLAG MUC_NS"#self-ping-optimization"
/**
* An XMPP component, communicating with an XMPP server using the protocole
@@ -134,8 +135,8 @@ public:
/**
* Send a (non-private) message to the MUC
*/
- void send_muc_message(const std::string& muc_name, const std::string& nick, Xmpp::body&& body, const std::string& jid_to,
- std::string uuid, std::string id);
+ Stanza 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);
#ifdef USE_DATABASE
/**
* Send a message, with a <delay/> element, part of a MUC history
diff --git a/src/xmpp/xmpp_parser.cpp b/src/xmpp/xmpp_parser.cpp
index 0488be9..781fe4c 100644
--- a/src/xmpp/xmpp_parser.cpp
+++ b/src/xmpp/xmpp_parser.cpp
@@ -20,7 +20,7 @@ static void end_element_handler(void* user_data, const XML_Char* name)
static void character_data_handler(void *user_data, const XML_Char *s, int len)
{
- static_cast<XmppParser*>(user_data)->char_data(s, len);
+ static_cast<XmppParser*>(user_data)->char_data(s, static_cast<std::size_t>(len));
}
/**