diff options
-rw-r--r-- | src/bridge/bridge.cpp | 66 | ||||
-rw-r--r-- | src/bridge/bridge.hpp | 2 | ||||
-rw-r--r-- | src/xmpp/xmpp_component.cpp | 20 |
3 files changed, 81 insertions, 7 deletions
diff --git a/src/bridge/bridge.cpp b/src/bridge/bridge.cpp index 5fa96c8..fc00c8c 100644 --- a/src/bridge/bridge.cpp +++ b/src/bridge/bridge.cpp @@ -183,6 +183,72 @@ void Bridge::send_channel_message(const Iid& iid, const std::string& body) } } +void Bridge::forward_affiliation_role_change(const Iid& iid, const std::string& nick, + const std::string& affiliation, + const std::string& role) +{ + IrcClient* irc = this->get_irc_client(iid.get_server()); + if (!irc) + return; + IrcChannel* chan = irc->get_channel(iid.get_local()); + if (!chan || !chan->joined) + return; + IrcUser* user = chan->find_user(nick); + if (!user) + return; + // For each affiliation or role, we have a “maximal” mode that we want to + // set. We must remove any superior mode at the same time. For example if + // the user already has +o mode, and we set its affiliation to member, we + // remove the +o mode, and add +v. For each “superior” mode (for example, + // for +v, the superior modes are 'h', 'a', 'o' and 'q') we check if that + // user has it, and if yes we remove that mode + + std::size_t nb = 1; // the number of times the nick must be + // repeated in the argument list + std::string modes; // The string of modes to + // add/remove. For example "+v-aoh" + std::vector<char> modes_to_remove; // List of modes to check for removal + if (affiliation == "none") + { + modes = ""; + nb = 0; + modes_to_remove = {'v', 'h', 'o', 'a', 'q'}; + } + else if (affiliation == "member") + { + modes = "+v"; + modes_to_remove = {'h', 'o', 'a', 'q'}; + } + else if (role == "moderator") + { + modes = "+h"; + modes_to_remove = {'o', 'a', 'q'}; + } + else if (affiliation == "admin") + { + modes = "+o"; + modes_to_remove = {'a', 'q'}; + } + else if (affiliation == "owner") + { + modes = "+a"; + modes_to_remove = {'q'}; + } + else + return; + for (const char mode: modes_to_remove) + if (user->modes.find(mode) != user->modes.end()) + { + modes += "-"s + mode; + nb++; + } + if (modes.empty()) + return; + std::vector<std::string> args(nb, nick); + args.insert(args.begin(), modes); + irc->send_mode_command(iid.get_local(), args); +} + void Bridge::send_private_message(const Iid& iid, const std::string& body, const std::string& type) { if (iid.get_local().empty() || iid.get_server().empty()) diff --git a/src/bridge/bridge.hpp b/src/bridge/bridge.hpp index 13cac23..b1f79d5 100644 --- a/src/bridge/bridge.hpp +++ b/src/bridge/bridge.hpp @@ -72,6 +72,8 @@ public: void send_irc_version_request(const std::string& irc_hostname, const std::string& target, const std::string& iq_id, const std::string& to_jid, const std::string& from_jid); + void forward_affiliation_role_change(const Iid& iid, const std::string& nick, + const std::string& affiliation, const std::string& role); /** * Directly send a CTCP PING request to the IRC user */ diff --git a/src/xmpp/xmpp_component.cpp b/src/xmpp/xmpp_component.cpp index c63dc00..0328c78 100644 --- a/src/xmpp/xmpp_component.cpp +++ b/src/xmpp/xmpp_component.cpp @@ -481,14 +481,20 @@ void XmppComponent::handle_iq(const Stanza& stanza) { std::string nick = child->get_tag("nick"); std::string role = child->get_tag("role"); - if (!nick.empty() && role == "none") - { // This is a kick - std::string reason; - XmlNode* reason_el = child->get_child("reason", MUC_ADMIN_NS); - if (reason_el) - reason = reason_el->get_inner(); + std::string affiliation = child->get_tag("affiliation"); + if (!nick.empty()) + { Iid iid(to.local); - bridge->send_irc_kick(iid, nick, reason, id, from); + if (role == "none") + { // This is a kick + std::string reason; + XmlNode* reason_el = child->get_child("reason", MUC_ADMIN_NS); + if (reason_el) + reason = reason_el->get_inner(); + bridge->send_irc_kick(iid, nick, reason, id, from); + } + else + bridge->forward_affiliation_role_change(iid, nick, affiliation, role); stanza_error.disable(); } } |