diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/bridge/bridge.cpp | 72 | ||||
-rw-r--r-- | src/irc/iid.cpp | 62 | ||||
-rw-r--r-- | src/irc/iid.hpp | 63 | ||||
-rw-r--r-- | src/irc/irc_client.cpp | 61 | ||||
-rw-r--r-- | src/test.cpp | 36 | ||||
-rw-r--r-- | src/xmpp/xmpp_component.cpp | 40 | ||||
-rw-r--r-- | src/xmpp/xmpp_component.hpp | 8 |
7 files changed, 253 insertions, 89 deletions
diff --git a/src/bridge/bridge.cpp b/src/bridge/bridge.cpp index 3377135..6d864c0 100644 --- a/src/bridge/bridge.cpp +++ b/src/bridge/bridge.cpp @@ -106,8 +106,8 @@ IrcClient* Bridge::get_irc_client(const std::string& hostname) bool Bridge::join_irc_channel(const Iid& iid, const std::string& username) { - IrcClient* irc = this->get_irc_client(iid.server, username); - if (iid.chan.empty()) + IrcClient* irc = this->get_irc_client(iid.get_server(), username); + if (iid.get_local().empty()) { // Join the dummy channel if (irc->is_welcomed()) { @@ -117,7 +117,7 @@ bool Bridge::join_irc_channel(const Iid& iid, const std::string& username) // joined the channel const IrcMessage join_message(irc->get_nick(), "JOIN", {""}); irc->on_channel_join(join_message); - const IrcMessage end_join_message(std::string(iid.server), "366", + const IrcMessage end_join_message(std::string(iid.get_server()), "366", {irc->get_nick(), "", "End of NAMES list"}); irc->on_channel_completely_joined(end_join_message); @@ -129,9 +129,9 @@ bool Bridge::join_irc_channel(const Iid& iid, const std::string& username) } return true; } - if (irc->is_channel_joined(iid.chan) == false) + if (irc->is_channel_joined(iid.get_local()) == false) { - irc->send_join_command(iid.chan); + irc->send_join_command(iid.get_local()); return true; } return false; @@ -139,15 +139,15 @@ bool Bridge::join_irc_channel(const Iid& iid, const std::string& username) void Bridge::send_channel_message(const Iid& iid, const std::string& body) { - if (iid.chan.empty() || iid.server.empty()) + if (iid.get_local().empty() || iid.get_server().empty()) { - log_warning("Cannot send message to channel: [" << iid.chan << "] on server [" << iid.server << "]"); + log_warning("Cannot send message to channel: [" << iid.get_local() << "] on server [" << iid.get_server() << "]"); return; } - IrcClient* irc = this->get_irc_client(iid.server); + IrcClient* irc = this->get_irc_client(iid.get_server()); if (!irc) { - log_warning("Cannot send message: no client exist for server " << iid.server); + log_warning("Cannot send message: no client exist for server " << iid.get_server()); return; } // Because an IRC message cannot contain \n, we need to convert each line @@ -166,27 +166,27 @@ void Bridge::send_channel_message(const Iid& iid, const std::string& body) if (line.substr(0, 6) == "/mode ") { std::vector<std::string> args = utils::split(line.substr(6), ' ', false); - irc->send_mode_command(iid.chan, args); + irc->send_mode_command(iid.get_local(), args); continue; // We do not want to send that back to the // XMPP user, that’s not a textual message. } else if (line.substr(0, 4) == "/me ") - irc->send_channel_message(iid.chan, action_prefix + line.substr(4) + "\01"); + irc->send_channel_message(iid.get_local(), action_prefix + line.substr(4) + "\01"); else - irc->send_channel_message(iid.chan, line); - this->xmpp->send_muc_message(iid.chan + "%" + iid.server, irc->get_own_nick(), + irc->send_channel_message(iid.get_local(), line); + this->xmpp->send_muc_message(std::to_string(iid), irc->get_own_nick(), this->make_xmpp_body(line), this->user_jid); } } void Bridge::send_private_message(const Iid& iid, const std::string& body, const std::string& type) { - if (iid.chan.empty() || iid.server.empty()) + if (iid.get_local().empty() || iid.get_server().empty()) return ; - IrcClient* irc = this->get_irc_client(iid.server); + IrcClient* irc = this->get_irc_client(iid.get_server()); if (!irc) { - log_warning("Cannot send message: no client exist for server " << iid.server); + log_warning("Cannot send message: no client exist for server " << iid.get_server()); return; } std::vector<std::string> lines = utils::split(body, '\n', true); @@ -195,38 +195,38 @@ void Bridge::send_private_message(const Iid& iid, const std::string& body, const for (const std::string& line: lines) { if (line.substr(0, 4) == "/me ") - irc->send_private_message(iid.chan, action_prefix + line.substr(4) + "\01", type); + irc->send_private_message(iid.get_local(), action_prefix + line.substr(4) + "\01", type); else - irc->send_private_message(iid.chan, line, type); + irc->send_private_message(iid.get_local(), line, type); } } void Bridge::leave_irc_channel(Iid&& iid, std::string&& status_message) { - IrcClient* irc = this->get_irc_client(iid.server); + IrcClient* irc = this->get_irc_client(iid.get_server()); if (irc) - irc->send_part_command(iid.chan, status_message); + irc->send_part_command(iid.get_local(), status_message); } void Bridge::send_irc_nick_change(const Iid& iid, const std::string& new_nick) { - IrcClient* irc = this->get_irc_client(iid.server); + IrcClient* irc = this->get_irc_client(iid.get_server()); if (irc) irc->send_nick_command(new_nick); } void Bridge::send_irc_kick(const Iid& iid, const std::string& target, const std::string& reason) { - IrcClient* irc = this->get_irc_client(iid.server); + IrcClient* irc = this->get_irc_client(iid.get_server()); if (irc) - irc->send_kick_command(iid.chan, target, reason); + irc->send_kick_command(iid.get_local(), target, reason); } void Bridge::set_channel_topic(const Iid& iid, const std::string& subject) { - IrcClient* irc = this->get_irc_client(iid.server); + IrcClient* irc = this->get_irc_client(iid.get_server()); if (irc) - irc->send_topic_command(iid.chan, subject); + irc->send_topic_command(iid.get_local(), subject); } void Bridge::send_xmpp_version_to_irc(const Iid& iid, const std::string& name, const std::string& version, const std::string& os) @@ -239,22 +239,22 @@ void Bridge::send_xmpp_version_to_irc(const Iid& iid, const std::string& name, c void Bridge::send_message(const Iid& iid, const std::string& nick, const std::string& body, const bool muc) { if (muc) - this->xmpp->send_muc_message(iid.chan + "%" + iid.server, nick, + this->xmpp->send_muc_message(std::to_string(iid), nick, this->make_xmpp_body(body), this->user_jid); else - this->xmpp->send_message(iid.chan + "%" + iid.server, + this->xmpp->send_message(std::to_string(iid), this->make_xmpp_body(body), this->user_jid, "chat"); } void Bridge::send_join_failed(const Iid& iid, const std::string& nick, const std::string& type, const std::string& condition, const std::string& text) { - this->xmpp->send_presence_error(iid.chan + "%" + iid.server, nick, this->user_jid, type, condition, text); + this->xmpp->send_presence_error(std::to_string(iid), nick, this->user_jid, type, condition, text); } void Bridge::send_muc_leave(Iid&& iid, std::string&& nick, const std::string& message, const bool self) { - this->xmpp->send_muc_leave(std::move(iid.chan) + "%" + std::move(iid.server), std::move(nick), this->make_xmpp_body(message), this->user_jid, self); - IrcClient* irc = this->get_irc_client(iid.server); + this->xmpp->send_muc_leave(std::to_string(iid), std::move(nick), this->make_xmpp_body(message), this->user_jid, self); + IrcClient* irc = this->get_irc_client(iid.get_server()); if (irc && irc->number_of_joined_channels() == 0) irc->send_quit_command(""); } @@ -269,7 +269,7 @@ void Bridge::send_nick_change(Iid&& iid, std::string role; std::tie(role, affiliation) = get_role_affiliation_from_irc_mode(user_mode); - this->xmpp->send_nick_change(std::move(iid.chan) + "%" + std::move(iid.server), + this->xmpp->send_nick_change(std::to_string(iid), old_nick, new_nick, affiliation, role, this->user_jid, self); } @@ -309,7 +309,7 @@ void Bridge::send_topic(const std::string& hostname, const std::string& chan_nam std::string Bridge::get_own_nick(const Iid& iid) { - IrcClient* irc = this->get_irc_client(iid.server); + IrcClient* irc = this->get_irc_client(iid.get_server()); if (irc) return irc->get_own_nick(); return ""; @@ -322,12 +322,12 @@ size_t Bridge::active_clients() const void Bridge::kick_muc_user(Iid&& iid, const std::string& target, const std::string& reason, const std::string& author) { - this->xmpp->kick_user(iid.chan + "%" + iid.server, target, reason, author, this->user_jid); + this->xmpp->kick_user(std::to_string(iid), target, reason, author, this->user_jid); } void Bridge::send_nickname_conflict_error(const Iid& iid, const std::string& nickname) { - this->xmpp->send_nickname_conflict_error(iid.chan + "%" + iid.server, nickname, this->user_jid); + this->xmpp->send_nickname_conflict_error(std::to_string(iid), nickname, this->user_jid); } void Bridge::send_affiliation_role_change(const Iid& iid, const std::string& target, const char mode) @@ -336,10 +336,10 @@ void Bridge::send_affiliation_role_change(const Iid& iid, const std::string& tar std::string affiliation; std::tie(role, affiliation) = get_role_affiliation_from_irc_mode(mode); - this->xmpp->send_affiliation_role_change(iid.chan + "%" + iid.server, target, affiliation, role, this->user_jid); + this->xmpp->send_affiliation_role_change(std::to_string(iid), target, affiliation, role, this->user_jid); } void Bridge::send_iq_version_request(const std::string& nick, const std::string& hostname) { - this->xmpp->send_iq_version_request(nick + "%" + hostname, this->user_jid); + this->xmpp->send_iq_version_request(nick + "!" + hostname, this->user_jid); } diff --git a/src/irc/iid.cpp b/src/irc/iid.cpp index 4694c0c..4893e9e 100644 --- a/src/irc/iid.cpp +++ b/src/irc/iid.cpp @@ -1,21 +1,63 @@ +#include <utils/tolower.hpp> + #include <irc/iid.hpp> -Iid::Iid(const std::string& iid) +Iid::Iid(const std::string& iid): + is_channel(false), + is_user(false) { - std::string::size_type sep = iid.find("%"); + const std::string::size_type sep = iid.find_first_of("%!"); if (sep != std::string::npos) { - this->chan = iid.substr(0, sep); - sep++; + if (iid[sep] == '%') + this->is_channel = true; + else + this->is_user = true; + this->set_local(iid.substr(0, sep)); + this->set_server(iid.substr(sep + 1)); } else - { - this->chan = iid; - return; - } - this->server = iid.substr(sep); + this->set_server(iid); +} + +Iid::Iid(): + is_channel(false), + is_user(false) +{ +} + +void Iid::set_local(const std::string& loc) +{ + this->local = utils::tolower(loc); +} + +void Iid::set_server(const std::string& serv) +{ + this->server = utils::tolower(serv); +} + +const std::string& Iid::get_local() const +{ + return this->local; } -Iid::Iid() +const std::string& Iid::get_server() const { + return this->server; +} + +std::string Iid::get_sep() const +{ + if (this->is_channel) + return "%"; + else if (this->is_user) + return "!"; + return ""; +} + +namespace std { + const std::string to_string(const Iid& iid) + { + return iid.get_local() + iid.get_sep() + iid.get_server(); + } } diff --git a/src/irc/iid.hpp b/src/irc/iid.hpp index a62ac71..2302a18 100644 --- a/src/irc/iid.hpp +++ b/src/irc/iid.hpp @@ -4,17 +4,40 @@ #include <string> /** - * A name representing an IRC channel, on the same model than the XMPP JIDs (but much simpler). - * The separator between the server and the channel name is '%' - * #test%irc.freenode.org has : - * - chan: "#test" (the # is part of the name, it could very well be absent, or & instead - * - server: "irc.freenode.org" - * #test has: - * - chan: "#test" - * - server: "" - * %irc.freenode.org: - * - chan: "" - * - server: "irc.freenode.org" + * A name representing an IRC channel on an IRC server, or an IRC user on an + * IRC server, or just an IRC server. + * + * The separator for an user is '!', for a channel it's '%'. If no separator + * is present, it's just an irc server. + * It’s possible to have an empty-string server, but it makes no sense in + * the biboumi context. + * + * #test%irc.example.org has : + * - local: "#test" (the # is part of the name, it could very well be absent, or & (for example) instead) + * - server: "irc.example.org" + * - is_channel: true + * - is_user: false + * + * %irc.example.org: + * - local: "" + * - server: "irc.example.org" + * - is_channel: true + * - is_user: false + * Note: this is the special empty-string channel, used internal in biboumi + * but has no meaning on IRC. + * + * foo!irc.example.org + * - local: "foo" + * - server: "irc.example.org" + * - is_channel: false + * - is_user: true + * Note: the empty-string user (!irc.example.org) has no special meaning in biboumi + * + * irc.example.org: + * - local: "" + * - server: "irc.example.org" + * - is_channel: false + * - is_user: false */ class Iid { @@ -22,14 +45,28 @@ public: explicit Iid(const std::string& iid); explicit Iid(); - std::string chan; - std::string server; + void set_local(const std::string& loc); + void set_server(const std::string& serv); + const std::string& get_local() const; + const std::string& get_server() const; + + bool is_channel; + bool is_user; + + std::string get_sep() const; private: + std::string local; + std::string server; + Iid(const Iid&) = delete; Iid(Iid&&) = delete; Iid& operator=(const Iid&) = delete; Iid& operator=(Iid&&) = delete; }; +namespace std { + const std::string to_string(const Iid& iid); +} + #endif // IID_INCLUDED diff --git a/src/irc/irc_client.cpp b/src/irc/irc_client.cpp index c3765a6..6e22980 100644 --- a/src/irc/irc_client.cpp +++ b/src/irc/irc_client.cpp @@ -102,10 +102,11 @@ void IrcClient::on_connection_close() log_warning(message); } -IrcChannel* IrcClient::get_channel(const std::string& name) +IrcChannel* IrcClient::get_channel(const std::string& n) { - if (name.empty()) + if (n.empty()) return &this->dummy_channel; + const std::string name = utils::tolower(n); try { return this->channels.at(name).get(); @@ -382,15 +383,18 @@ void IrcClient::on_channel_message(const IrcMessage& message) const IrcUser user(message.prefix); const std::string nick = user.nick; Iid iid; - iid.chan = utils::tolower(message.arguments[0]); - iid.server = this->hostname; + iid.set_local(message.arguments[0]); + iid.set_server(this->hostname); const std::string body = message.arguments[1]; bool muc = true; - if (!this->get_channel(iid.chan)->joined) + if (!this->get_channel(iid.get_local())->joined) { - iid.chan = nick; + iid.is_user = true; + iid.set_local(nick); muc = false; } + else + iid.is_channel = true; if (!body.empty() && body[0] == '\01') { if (body.substr(1, 6) == "ACTION") @@ -460,8 +464,9 @@ void IrcClient::on_nickname_conflict(const IrcMessage& message) for (auto it = this->channels.begin(); it != this->channels.end(); ++it) { Iid iid; - iid.chan = it->first; - iid.server = this->hostname; + iid.set_local(it->first); + iid.set_server(this->hostname); + iid.is_channel = true; this->bridge->send_nickname_conflict_error(iid, nickname); } } @@ -499,7 +504,7 @@ void IrcClient::on_welcome_message(const IrcMessage& message) void IrcClient::on_part(const IrcMessage& message) { - const std::string chan_name = utils::tolower(message.arguments[0]); + const std::string chan_name = message.arguments[0]; IrcChannel* channel = this->get_channel(chan_name); if (!channel->joined) return ; @@ -512,13 +517,14 @@ void IrcClient::on_part(const IrcMessage& message) std::string nick = user->nick; channel->remove_user(user); Iid iid; - iid.chan = chan_name; - iid.server = this->hostname; + iid.set_local(chan_name); + iid.set_server(this->hostname); + iid.is_channel = true; bool self = channel->get_self()->nick == nick; if (self) { channel->joined = false; - this->channels.erase(chan_name); + this->channels.erase(utils::tolower(chan_name)); // channel pointer is now invalid channel = nullptr; } @@ -533,8 +539,9 @@ void IrcClient::on_error(const IrcMessage& message) for (auto it = this->channels.begin(); it != this->channels.end(); ++it) { Iid iid; - iid.chan = it->first; - iid.server = this->hostname; + iid.set_local(it->first); + iid.set_server(this->hostname); + iid.is_channel = true; IrcChannel* channel = it->second.get(); if (!channel->joined) continue; @@ -560,8 +567,9 @@ void IrcClient::on_quit(const IrcMessage& message) std::string nick = user->nick; channel->remove_user(user); Iid iid; - iid.chan = chan_name; - iid.server = this->hostname; + iid.set_local(chan_name); + iid.set_server(this->hostname); + iid.is_channel = true; this->bridge->send_muc_leave(std::move(iid), std::move(nick), txt, false); } } @@ -579,8 +587,9 @@ void IrcClient::on_nick(const IrcMessage& message) { std::string old_nick = user->nick; Iid iid; - iid.chan = chan_name; - iid.server = this->hostname; + iid.set_local(chan_name); + iid.set_server(this->hostname); + iid.is_channel = true; const bool self = channel->get_self()->nick == old_nick; const char user_mode = user->get_most_significant_mode(this->sorted_user_modes); this->bridge->send_nick_change(std::move(iid), old_nick, new_nick, user_mode, self); @@ -606,8 +615,9 @@ void IrcClient::on_kick(const IrcMessage& message) channel->joined = false; IrcUser author(message.prefix); Iid iid; - iid.chan = chan_name; - iid.server = this->hostname; + iid.set_local(chan_name); + iid.set_server(this->hostname); + iid.is_channel = true; this->bridge->kick_muc_user(std::move(iid), target, reason, author.nick); } @@ -625,8 +635,9 @@ void IrcClient::on_channel_mode(const IrcMessage& message) // For now, just transmit the modes so the user can know what happens // TODO, actually interprete the mode. Iid iid; - iid.chan = utils::tolower(message.arguments[0]); - iid.server = this->hostname; + iid.set_local(message.arguments[0]); + iid.set_server(this->hostname); + iid.is_channel = true; IrcUser user(message.prefix); std::string mode_arguments; for (size_t i = 1; i < message.arguments.size(); ++i) @@ -638,10 +649,10 @@ void IrcClient::on_channel_mode(const IrcMessage& message) mode_arguments += message.arguments[i]; } } - this->bridge->send_message(iid, "", "Mode "s + iid.chan + + this->bridge->send_message(iid, "", "Mode "s + iid.get_local() + " [" + mode_arguments + "] by " + user.nick, true); - const IrcChannel* channel = this->get_channel(iid.chan); + const IrcChannel* channel = this->get_channel(iid.get_local()); if (!channel) return; @@ -695,7 +706,7 @@ void IrcClient::on_channel_mode(const IrcMessage& message) if (!user) { log_warning("Trying to set mode for non-existing user '" << target - << "' in channel" << iid.chan); + << "' in channel" << iid.get_local()); return; } if (add) diff --git a/src/test.cpp b/src/test.cpp index 216608a..87ab49c 100644 --- a/src/test.cpp +++ b/src/test.cpp @@ -290,5 +290,41 @@ int main() std::cout << correctjid2 << std::endl; assert(correctjid2 == "zigougou@poez.io"); + /** + * IID parsing + */ + std::cout << color << "Testing IID parsing…" << reset << std::endl; + Iid iid1("foo!irc.example.org"); + std::cout << std::to_string(iid1) << std::endl; + assert(std::to_string(iid1) == "foo!irc.example.org"); + assert(iid1.get_local() == "foo"); + assert(iid1.get_server() == "irc.example.org"); + assert(!iid1.is_channel); + assert(iid1.is_user); + + Iid iid2("#test%irc.example.org"); + std::cout << std::to_string(iid2) << std::endl; + assert(std::to_string(iid2) == "#test%irc.example.org"); + assert(iid2.get_local() == "#test"); + assert(iid2.get_server() == "irc.example.org"); + assert(iid2.is_channel); + assert(!iid2.is_user); + + Iid iid3("%irc.example.org"); + std::cout << std::to_string(iid3) << std::endl; + assert(std::to_string(iid3) == "%irc.example.org"); + assert(iid3.get_local() == ""); + assert(iid3.get_server() == "irc.example.org"); + assert(iid3.is_channel); + assert(!iid3.is_user); + + Iid iid4("irc.example.org"); + std::cout << std::to_string(iid4) << std::endl; + assert(std::to_string(iid4) == "irc.example.org"); + assert(iid4.get_local() == ""); + assert(iid4.get_server() == "irc.example.org"); + assert(!iid4.is_channel); + assert(!iid4.is_user); + return 0; } diff --git a/src/xmpp/xmpp_component.cpp b/src/xmpp/xmpp_component.cpp index 4c9c8ff..ec8517c 100644 --- a/src/xmpp/xmpp_component.cpp +++ b/src/xmpp/xmpp_component.cpp @@ -306,7 +306,7 @@ void XmppComponent::handle_presence(const Stanza& stanza) error_type, error_name, ""); }); - if (!iid.server.empty()) + if (iid.is_channel && !iid.get_server().empty()) { // presence toward a MUC that corresponds to an irc channel, or a // dummy channel if iid.chan is empty if (type.empty()) @@ -353,7 +353,7 @@ void XmppComponent::handle_message(const Stanza& stanza) error_type, error_name, ""); }); XmlNode* body = stanza.get_child("body", COMPONENT_NS); - if (type == "groupchat") + if (type == "groupchat" && iid.is_channel) { if (to.resource.empty()) if (body && !body->get_inner().empty()) @@ -379,11 +379,13 @@ void XmppComponent::handle_message(const Stanza& stanza) if (kickable_error) bridge->shutdown("Error from remote client"); } - else if (type == "chat") + else if (type == "chat" && iid.is_user && !iid.get_local().empty()) { if (body && !body->get_inner().empty()) bridge->send_private_message(iid, body->get_inner()); } + else if (iid.is_user) + this->send_invalid_user_error(to.local, from); stanza_error.disable(); } @@ -671,6 +673,36 @@ void XmppComponent::send_invalid_room_error(const std::string& muc_name, this->send_stanza(presence); } +void XmppComponent::send_invalid_user_error(const std::string& user_name, const std::string& to) +{ + Stanza message("message"); + message["from"] = user_name + "@" + this->served_hostname; + message["to"] = to; + message["type"] = "error"; + XmlNode x("x"); + x["xmlns"] = MUC_NS; + x.close(); + message.add_child(std::move(x)); + XmlNode error("error"); + error["type"] = "cancel"; + XmlNode item_not_found("item-not-found"); + item_not_found["xmlns"] = STANZA_NS; + item_not_found.close(); + error.add_child(std::move(item_not_found)); + XmlNode text("text"); + text["xmlns"] = STANZA_NS; + text["xml:lang"] = "en"; + text.set_inner(user_name + + " is not a valid IRC user name. A correct user jid is of the form: <nick>!<server>@" + + this->served_hostname); + text.close(); + error.add_child(std::move(text)); + error.close(); + message.add_child(std::move(error)); + message.close(); + this->send_stanza(message); +} + void XmppComponent::send_topic(const std::string& from, Xmpp::body&& topic, const std::string& to) { XmlNode message("message"); @@ -711,7 +743,7 @@ void XmppComponent::send_muc_message(const std::string& muc_name, const std::str this->send_stanza(message); } -void XmppComponent::send_muc_leave(std::string&& muc_name, std::string&& nick, Xmpp::body&& message, const std::string& jid_to, const bool self) +void XmppComponent::send_muc_leave(const std::string& muc_name, std::string&& nick, Xmpp::body&& message, const std::string& jid_to, const bool self) { Stanza presence("presence"); presence["to"] = jid_to; diff --git a/src/xmpp/xmpp_component.hpp b/src/xmpp/xmpp_component.hpp index 5de471c..f1806de 100644 --- a/src/xmpp/xmpp_component.hpp +++ b/src/xmpp/xmpp_component.hpp @@ -131,6 +131,12 @@ public: const std::string& nick, const std::string& to); /** + * Send an error to indicate that the user tried to send a message to an + * invalid user. + */ + void send_invalid_user_error(const std::string& user_name, + const std::string& to); + /** * Send the MUC topic to the user */ void send_topic(const std::string& from, Xmpp::body&& xmpp_topic, const std::string& to); @@ -141,7 +147,7 @@ public: /** * Send an unavailable presence for this nick */ - void send_muc_leave(std::string&& muc_name, std::string&& nick, Xmpp::body&& message, const std::string& jid_to, const bool self); + void send_muc_leave(const std::string& muc_name, std::string&& nick, Xmpp::body&& message, const std::string& jid_to, const bool self); /** * Indicate that a participant changed his nick */ |