diff options
author | louiz’ <louiz@louiz.org> | 2018-09-03 22:26:57 +0200 |
---|---|---|
committer | louiz’ <louiz@louiz.org> | 2018-09-03 22:26:57 +0200 |
commit | 193302b0de20df6adc090eaeaa84cfd286be724a (patch) | |
tree | b9e0e0f75b5cd74911a42a7fcd0f1477971391ff /src/xmpp | |
parent | 56651cb5c29cc50ddf3c62c37167fa0b9389bfde (diff) | |
parent | 28acbed948e1c281f9de6132164e42d0ed20c32f (diff) | |
download | biboumi-193302b0de20df6adc090eaeaa84cfd286be724a.tar.gz biboumi-193302b0de20df6adc090eaeaa84cfd286be724a.tar.bz2 biboumi-193302b0de20df6adc090eaeaa84cfd286be724a.tar.xz biboumi-193302b0de20df6adc090eaeaa84cfd286be724a.zip |
Merge branch 'master' into debian
Diffstat (limited to 'src/xmpp')
-rw-r--r-- | src/xmpp/adhoc_commands_handler.cpp | 2 | ||||
-rw-r--r-- | src/xmpp/biboumi_adhoc_commands.cpp | 140 | ||||
-rw-r--r-- | src/xmpp/biboumi_component.cpp | 145 | ||||
-rw-r--r-- | src/xmpp/biboumi_component.hpp | 3 | ||||
-rw-r--r-- | src/xmpp/jid.cpp | 2 | ||||
-rw-r--r-- | src/xmpp/xmpp_component.cpp | 18 | ||||
-rw-r--r-- | src/xmpp/xmpp_component.hpp | 6 | ||||
-rw-r--r-- | src/xmpp/xmpp_parser.cpp | 2 |
8 files changed, 231 insertions, 87 deletions
diff --git a/src/xmpp/adhoc_commands_handler.cpp b/src/xmpp/adhoc_commands_handler.cpp index bb48781..bc4c108 100644 --- a/src/xmpp/adhoc_commands_handler.cpp +++ b/src/xmpp/adhoc_commands_handler.cpp @@ -41,7 +41,7 @@ XmlNode AdhocCommandsHandler::handle_request(const std::string& executor_jid, co XmlSubNode condition(error, STANZA_NS":item-not-found"); } else if (command_it->second.is_admin_only() && - Config::get("admin", "") != jid.local + "@" + jid.domain) + !Config::is_in_list("admin", jid.bare())) { XmlSubNode error(command_node, ADHOC_NS":error"); error["type"] = "cancel"; diff --git a/src/xmpp/biboumi_adhoc_commands.cpp b/src/xmpp/biboumi_adhoc_commands.cpp index bcdac39..7c31f36 100644 --- a/src/xmpp/biboumi_adhoc_commands.cpp +++ b/src/xmpp/biboumi_adhoc_commands.cpp @@ -14,6 +14,14 @@ #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 @@ -115,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"); @@ -127,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>())); @@ -139,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"); @@ -155,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"); @@ -176,6 +185,7 @@ void ConfigureGlobalStep2(XmppComponent& xmpp_component, AdhocSession& session, { const Jid owner(session.get_owner_jid()); auto options = Database::get_global_options(owner.bare()); + options.clear(); for (const XmlNode* field: x->get_children("field", "jabber:x:data")) { const XmlNode* value = field->get_child("value", "jabber:x:data"); @@ -196,7 +206,7 @@ void ConfigureGlobalStep2(XmppComponent& xmpp_component, AdhocSession& session, options.col<Database::GlobalPersistent>() = to_bool(value->get_inner()); } - options.save(Database::db); + save(options, *Database::db); command_node.delete_all_children(); XmlSubNode note(command_node, "note"); @@ -219,7 +229,9 @@ void ConfigureIrcServerStep1(XmppComponent&, AdhocSession& session, XmlNode& com server_domain = target.local; auto options = Database::get_irc_server_options(owner.local + "@" + owner.domain, 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"); @@ -227,12 +239,26 @@ void ConfigureIrcServerStep1(XmppComponent&, AdhocSession& session, XmlNode& com XmlSubNode instructions(x, "instructions"); instructions.set_inner("Edit the form, to configure the settings of the IRC server " + server_domain); + if (Config::get("fixed_irc_server", "").empty()) + { + XmlSubNode field(x, "field"); + field["var"] = "address"; + field["type"] = "text-single"; + field["label"] = "Address"; + 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); + else + value.set_inner(options.col<Database::Address>()); + } + { XmlSubNode ports(x, "field"); 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"); @@ -246,7 +272,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"); @@ -259,7 +285,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"); @@ -279,12 +305,26 @@ void ConfigureIrcServerStep1(XmppComponent&, AdhocSession& session, XmlNode& com } } #endif + + { + XmlSubNode field(x, "field"); + field["var"] = "nick"; + field["type"] = "text-single"; + field["label"] = "Nickname"; + 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"); + value.set_inner(options.col<Database::Nick>()); + } + } + { XmlSubNode pass(x, "field"); 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"); @@ -294,14 +334,14 @@ void ConfigureIrcServerStep1(XmppComponent&, AdhocSession& session, XmlNode& com { XmlSubNode after_cnt_cmd(x, "field"); - after_cnt_cmd["var"] = "after_connect_command"; - after_cnt_cmd["type"] = "text-single"; - after_cnt_cmd["desc"] = "Custom IRC command sent after the connection is established with the server."; - after_cnt_cmd["label"] = "After-connection IRC command"; - if (!options.col<Database::AfterConnectionCommand>().empty()) + after_cnt_cmd["var"] = "after_connect_commands"; + after_cnt_cmd["type"] = "text-multi"; + 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) { XmlSubNode after_cnt_cmd_value(after_cnt_cmd, "value"); - after_cnt_cmd_value.set_inner(options.col<Database::AfterConnectionCommand>()); + after_cnt_cmd_value.set_inner(command.col<Database::AfterConnectionCommand>()); } } @@ -333,10 +373,19 @@ 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 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()) { @@ -349,7 +398,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()) { @@ -359,8 +408,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) { @@ -371,10 +422,17 @@ void ConfigureIrcServerStep2(XmppComponent&, AdhocSession& session, XmlNode& com server_domain = target.local; auto options = Database::get_irc_server_options(owner.local + "@" + owner.domain, server_domain); + options.clear(); + Database::AfterConnectionCommands commands{}; + for (const XmlNode* field: x->get_children("field", "jabber:x:data")) { const XmlNode* value = field->get_child("value", "jabber:x:data"); const std::vector<const XmlNode*> values = field->get_children("value", "jabber:x:data"); + + if (field->get_tag("var") == "address" && value && Config::get("fixed_irc_server", "").empty()) + options.col<Database::Address>() = value->get_inner(); + if (field->get_tag("var") == "ports") { std::string ports; @@ -406,11 +464,22 @@ void ConfigureIrcServerStep2(XmppComponent&, AdhocSession& session, XmlNode& com #endif // BOTAN_FOUND + else if (field->get_tag("var") == "nick" && value) + options.col<Database::Nick>() = value->get_inner(); + else if (field->get_tag("var") == "pass" && value) options.col<Database::Pass>() = value->get_inner(); - else if (field->get_tag("var") == "after_connect_command" && value) - options.col<Database::AfterConnectionCommand>() = value->get_inner(); + else if (field->get_tag("var") == "after_connect_commands") + { + commands.clear(); + for (const auto& val: values) + { + auto command = Database::after_connection_commands.row(); + command.col<Database::AfterConnectionCommand>() = val->get_inner(); + commands.push_back(std::move(command)); + } + } else if (field->get_tag("var") == "username" && value) { @@ -423,6 +492,22 @@ 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>() = -1; + } + 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") == "encoding_out" && value) options.col<Database::EncodingOut>() = value->get_inner(); @@ -431,7 +516,8 @@ void ConfigureIrcServerStep2(XmppComponent&, AdhocSession& session, XmlNode& com } Database::invalidate_encoding_in_cache(); - options.save(Database::db); + save(options, *Database::db); + Database::set_after_connection_commands(options, commands); command_node.delete_all_children(); XmlSubNode note(command_node, "note"); @@ -459,6 +545,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"); @@ -471,7 +558,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"); @@ -491,7 +578,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()) { @@ -504,7 +591,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()) { @@ -517,7 +604,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"); @@ -561,6 +648,7 @@ bool handle_irc_channel_configuration_form(XmppComponent& xmpp_component, const const Iid iid(target.local, {}); auto options = Database::get_irc_channel_options(requester.bare(), iid.get_server(), iid.get_local()); + options.clear(); for (const XmlNode *field: x->get_children("field", "jabber:x:data")) { const XmlNode *value = field->get_child("value", "jabber:x:data"); @@ -600,7 +688,7 @@ bool handle_irc_channel_configuration_form(XmppComponent& xmpp_component, const } Database::invalidate_encoding_in_cache(requester.bare(), iid.get_server(), iid.get_local()); - options.save(Database::db); + save(options, *Database::db); } return true; } @@ -611,7 +699,7 @@ bool handle_irc_channel_configuration_form(XmppComponent& xmpp_component, const void DisconnectUserFromServerStep1(XmppComponent& xmpp_component, AdhocSession& session, XmlNode& command_node) { const Jid owner(session.get_owner_jid()); - if (owner.bare() != Config::get("admin", "")) + if (!Config::is_in_list("admin", owner.bare())) { // A non-admin is not allowed to disconnect other users, only // him/herself, so we just skip this step auto next_step = session.get_next_step(); diff --git a/src/xmpp/biboumi_component.cpp b/src/xmpp/biboumi_component.cpp index 481ebb9..85617e8 100644 --- a/src/xmpp/biboumi_component.cpp +++ b/src/xmpp/biboumi_component.cpp @@ -72,11 +72,16 @@ BiboumiComponent::BiboumiComponent(std::shared_ptr<Poller>& poller, const std::s AdhocCommand configure_global_command({&ConfigureGlobalStep1, &ConfigureGlobalStep2}, "Configure a few settings", false); if (!Config::get("fixed_irc_server", "").empty()) - this->adhoc_commands_handler.add_command("configure", configure_server_command); + { + this->adhoc_commands_handler.add_command("server-configure", configure_server_command); + this->adhoc_commands_handler.add_command("global-configure", configure_global_command); + } else - this->adhoc_commands_handler.add_command("configure", configure_global_command); + { + this->adhoc_commands_handler.add_command("configure", configure_global_command); + this->irc_server_adhoc_commands_handler.add_command("configure", configure_server_command); + } - this->irc_server_adhoc_commands_handler.add_command("configure", configure_server_command); this->irc_channel_adhoc_commands_handler.add_command("configure", {{&ConfigureIrcChannelStep1, &ConfigureIrcChannelStep2}, "Configure a few settings for that IRC channel", false}); #endif } @@ -97,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) @@ -148,13 +153,10 @@ void BiboumiComponent::handle_presence(const Stanza& stanza) try { if (iid.type == Iid::Type::Channel && !iid.get_server().empty()) - { // presence toward a MUC that corresponds to an irc channel, or a - // dummy channel if iid.chan is empty + { // presence toward a MUC that corresponds to an irc channel if (type.empty()) { const std::string own_nick = bridge->get_own_nick(iid); - if (!own_nick.empty() && own_nick != to.resource) - bridge->send_irc_nick_change(iid, to.resource, from.resource); 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; @@ -182,7 +184,9 @@ void BiboumiComponent::handle_presence(const Stanza& stanza) history_limit.stanzas = 0; } bridge->join_irc_channel(iid, to.resource, password ? password->get_inner(): "", - from.resource, history_limit); + 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") { @@ -269,9 +273,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); @@ -280,7 +285,15 @@ void BiboumiComponent::handle_message(const Stanza& stanza) { if (body && !body->get_inner().empty()) { - bridge->send_channel_message(iid, body->get_inner()); + if (bridge->is_resource_in_chan(iid.to_tuple(), from.resource)) + bridge->send_channel_message(iid, body->get_inner(), id); + 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) @@ -346,7 +359,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) { @@ -467,8 +479,13 @@ void BiboumiComponent::handle_iq(const Stanza& stanza) #ifdef USE_DATABASE else if ((query = stanza.get_child("query", MAM_NS))) { - if (this->handle_mam_request(stanza)) - stanza_error.disable(); + try { + if (this->handle_mam_request(stanza)) + stanza_error.disable(); + } catch (const Database::RecordNotFound& exc) { + error_name = "item-not-found"; + return; + } } else if ((query = stanza.get_child("query", MUC_OWNER_NS))) { @@ -505,7 +522,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) @@ -547,24 +568,21 @@ void BiboumiComponent::handle_iq(const Stanza& stanza) if (to.local.empty()) { // Get biboumi's adhoc commands this->send_adhoc_commands_list(id, from, this->served_hostname, - (Config::get("admin", "") == - from_jid.bare()), + Config::is_in_list("admin", from_jid.bare()), this->adhoc_commands_handler); stanza_error.disable(); } else if (iid.type == Iid::Type::Server) { // Get the server's adhoc commands this->send_adhoc_commands_list(id, from, to_str, - (Config::get("admin", "") == - from_jid.bare()), + Config::is_in_list("admin", from_jid.bare()), this->irc_server_adhoc_commands_handler); stanza_error.disable(); } else if (iid.type == Iid::Type::Channel && to.resource.empty()) { // Get the channel's adhoc commands this->send_adhoc_commands_list(id, from, to_str, - (Config::get("admin", "") == - from_jid.bare()), + Config::is_in_list("admin", from_jid.bare()), this->irc_channel_adhoc_commands_handler); stanza_error.disable(); } @@ -586,7 +604,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; @@ -715,32 +732,47 @@ bool BiboumiComponent::handle_mam_request(const Stanza& stanza) } const XmlNode* set = query->get_child("set", RSM_NS); int limit = -1; + Id::real_type reference_record_id{Id::unset_value}; + Database::Paging paging_order{Database::Paging::first}; if (set) { const XmlNode* max = set->get_child("max", RSM_NS); if (max) limit = std::atoi(max->get_inner().data()); + const XmlNode* after = set->get_child("after", RSM_NS); + if (after) + { + auto after_record = Database::get_muc_log(from.bare(), iid.get_local(), iid.get_server(), + after->get_inner(), start, end); + reference_record_id = after_record.col<Id>(); + } + const XmlNode* before = set->get_child("before", RSM_NS); + if (before) + { + paging_order = Database::Paging::last; + if (!before->get_inner().empty()) + { + auto before_record = Database::get_muc_log(from.bare(), iid.get_local(), iid.get_server(), before->get_inner(), start, end); + reference_record_id = before_record.col<Id>(); + } + } } - // Do send more than 100 messages, even if the client asked for more, + // Do not send more than 100 messages, even if the client asked for more, // or if it didn’t specify any limit. - // 101 is just a trick to know if there are more available messages. - // If our query returns 101 message, we know it’s incomplete, but we - // still send only 100 - if ((limit == -1 && start.empty() && end.empty()) - || limit > 100) - limit = 101; - auto lines = Database::get_muc_logs(from.bare(), iid.get_local(), iid.get_server(), limit, start, end); - bool complete = true; - if (lines.size() > 100) + if (limit < 0 || limit > 100) + limit = 100; + auto result = Database::get_muc_logs(from.bare(), iid.get_local(), iid.get_server(), + static_cast<std::size_t>(limit), + start, end, + reference_record_id, paging_order); + bool complete = std::get<bool>(result); + auto& lines = std::get<1>(result); + + for (const Database::MucLogLine& line: lines) { - complete = false; - lines.erase(lines.begin(), std::prev(lines.end(), 100)); + if (!line.col<Database::Nick>().empty()) + this->send_archived_message(line, to.full(), from.full(), query_id); } - for (const Database::MucLogLine& line: lines) - { - if (!line.col<Database::Nick>().empty()) - this->send_archived_message(line, to.full(), from.full(), query_id); - } { auto fin_ptr = std::make_unique<XmlNode>("fin"); { @@ -892,7 +924,7 @@ void BiboumiComponent::send_self_disco_info(const std::string& id, const std::st identity["category"] = "conference"; identity["type"] = "irc"; identity["name"] = "Biboumi XMPP-IRC gateway"; - for (const char *ns: {DISCO_INFO_NS, MUC_NS, ADHOC_NS, PING_NS, MAM_NS, VERSION_NS}) + for (const char *ns: {DISCO_INFO_NS, MUC_NS, ADHOC_NS, PING_NS, MAM_NS, VERSION_NS, STABLE_MUC_ID_NS}) { XmlSubNode feature(query, "feature"); feature["var"] = ns; @@ -916,7 +948,7 @@ void BiboumiComponent::send_irc_server_disco_info(const std::string& id, const s identity["category"] = "conference"; identity["type"] = "irc"; identity["name"] = "IRC server " + from.local + " over Biboumi"; - for (const char *ns: {DISCO_INFO_NS, MUC_NS, ADHOC_NS, PING_NS, MAM_NS, VERSION_NS}) + for (const char *ns: {DISCO_INFO_NS, MUC_NS, ADHOC_NS, PING_NS, MAM_NS, VERSION_NS, STABLE_MUC_ID_NS}) { XmlSubNode feature(query, "feature"); feature["var"] = ns; @@ -943,7 +975,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, {}); @@ -958,12 +991,32 @@ void BiboumiComponent::send_irc_channel_disco_info(const std::string& id, const XmlSubNode identity(query, "identity"); identity["category"] = "conference"; identity["type"] = "irc"; - identity["name"] = "IRC channel " + iid.get_local() + " from server " + iid.get_server() + " over biboumi"; - for (const char *ns: {DISCO_INFO_NS, MUC_NS, ADHOC_NS, PING_NS, MAM_NS, VERSION_NS}) + 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}) { 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 9be9e34..b3d925e 100644 --- a/src/xmpp/xmpp_component.cpp +++ b/src/xmpp/xmpp_component.cpp @@ -2,6 +2,7 @@ #include <utils/scopeguard.hpp> #include <utils/tolower.hpp> #include <logger/logger.hpp> +#include <utils/uuid.hpp> #include <xmpp/xmpp_component.hpp> #include <config/config.hpp> @@ -14,8 +15,6 @@ #include <iostream> #include <set> -#include <uuid/uuid.h> - #include <cstdlib> #include <set> @@ -364,10 +363,11 @@ 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) +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 message("message"); message["to"] = jid_to; + message["id"] = std::move(id); if (!nick.empty()) message["from"] = muc_name + "@" + this->served_hostname + "/" + nick; else // Message from the room itself @@ -425,7 +425,8 @@ void XmppComponent::send_history_message(const std::string& muc_name, const std: #endif void XmppComponent::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& jid_to, const bool self, const bool user_requested, + const std::string& affiliation, const std::string& role) { Stanza presence("presence"); { @@ -447,6 +448,9 @@ void XmppComponent::send_muc_leave(const std::string& muc_name, const std::strin status["code"] = "332"; } } + XmlSubNode item(x, "item"); + item["affiliation"] = affiliation; + item["role"] = role; if (!message_str.empty()) { XmlSubNode status(presence, "status"); @@ -669,9 +673,5 @@ void XmppComponent::send_iq_result(const std::string& id, const std::string& to_ std::string XmppComponent::next_id() { - char uuid_str[37]; - uuid_t uuid; - uuid_generate(uuid); - uuid_unparse(uuid, uuid_str); - return uuid_str; + return utils::gen_uuid(); } diff --git a/src/xmpp/xmpp_component.hpp b/src/xmpp/xmpp_component.hpp index 1daa6fb..e18da40 100644 --- a/src/xmpp/xmpp_component.hpp +++ b/src/xmpp/xmpp_component.hpp @@ -37,6 +37,7 @@ #define RSM_NS "http://jabber.org/protocol/rsm" #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" /** * An XMPP component, communicating with an XMPP server using the protocole @@ -134,7 +135,7 @@ 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 uuid, std::string id); #ifdef USE_DATABASE /** * Send a message, with a <delay/> element, part of a MUC history @@ -150,7 +151,8 @@ public: Xmpp::body&& message, const std::string& jid_to, const bool self, - const bool user_requested); + const bool user_requested, + const std::string& affiliation, const std::string& role); /** * Indicate that a participant changed his nick */ 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)); } /** |