Commit 7c1a3899 authored by louiz’'s avatar louiz’

Rewrite the whole IID usage

IRC users and channels are now distinguished by the separator used in the
IID (% or !).

ref #2468
parent 6210a9d5
......@@ -101,11 +101,12 @@ signal again or if a 2 seconds delay has passed.
### Addressing
IRC entities are represented by XMPP JIDs. The domain part of the JID is
the domain served by biboumi, and the local part depends on the concerned
entity.
the domain served by biboumi (the part after the `@`, biboumi.example.com in
the examples), and the local part (the part before the `@`) depends on the
concerned entity.
IRC channels and IRC users JIDs have a localpart formed like this:
`name`, the `'%'` separator and the `irc_server`.
IRC channels have a local part formed like this:
`channel_name`%`irc_server`.
If the IRC channel you want to adress starts with the `'#'` character (or an
other character, announced by the IRC server, like `'&'`, `'+'` or `'!'`),
......@@ -113,12 +114,16 @@ then you must include it in the JID. Some other gateway implementations, as
well as some IRC clients, do not require them to be started by one of these
characters, adding an implicit `'#'` in that case. Biboumi does not do that
because this gets confusing when trying to understand the difference between
the channels *#foo*, and *##foo*.
the channels *#foo*, and *##foo*. Note that biboumi does not use the
presence of these special characters to identify an IRC channel, only the
presence of the separator `%` is used for that.
The name part can also be empty (for example `%irc.example.com`), in that
The channel name can also be empty (for example `%irc.example.com`), in that
case this represents the virtual channel provided by biboumi. See *Connect
to an IRC server* for more details.
IRC users have a local part formed like this:
`user_name`!`irc_server`.
On XMPP, the node part of the JID can only be lowercase. On the other hand,
IRC nicknames are case-insensitive, this means that the nicknames toto,
......@@ -131,7 +136,7 @@ Examples:
irc.example.com IRC server, and this is served by the biboumi instance on
biboumi.example.com
`toto%irc.example.com@biboumi.example.com` is the IRC user named toto, or
`toto!irc.example.com@biboumi.example.com` is the IRC user named toto, or
TotO, etc.
`irc.example.com@biboumi.example.com` is the IRC server irc.example.com.
......@@ -139,9 +144,20 @@ Examples:
`%irc.example.com@biboumi.example.com` is the virtual channel provided by
biboumi, for the IRC server irc.example.com.
If compiled with Libidn, an IRC user has a bare JID representing the
“hostname” provided by the IRC server. This JID can only be used to set IRC
modes (for example to ban a user based on its IP), or to identify user.
Note: Some JIDs are valid but make no sense in the context of
biboumi:
`!irc.example.com@biboumi.example.com` is the empty-string nick on the
irc.example.com server. It makes no sense to try to send messages to it.
`#test%@biboumi.example.com`, or any other JID that does not contain an
IRC server is invalid. Any message to that kind of JID will trigger an
error, or will be ignored.
If compiled with Libidn, an IRC channel participant has a bare JID
representing the “hostname” provided by the IRC server. This JID can only
be used to set IRC modes (for example to ban a user based on its IP), or to
identify user. It cannot be used to contact that user using biboumi.
### Join an IRC channel
......
......@@ -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);
}
#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();
}
}
......@@ -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
......@@ -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)
......
......@@ -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;
}
......@@ -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)
{