Commit 0d2dd71d authored by louiz’'s avatar louiz’

Don’t use ! as the separator for nicknames, use % instead

It’s now easier to use. The distinction between a nick and a channel name is
based on the first character (by default it's '#' and '&'). The user doesn’t
have to worry about which separator to use anymore.

fix #3066
parent 183f53d0
...@@ -133,7 +133,7 @@ IrcClient* Bridge::get_irc_client(const std::string& hostname) ...@@ -133,7 +133,7 @@ IrcClient* Bridge::get_irc_client(const std::string& hostname)
} }
} }
IrcClient* Bridge::find_irc_client(const std::string& hostname) IrcClient* Bridge::find_irc_client(const std::string& hostname) const
{ {
try try
{ {
...@@ -470,7 +470,7 @@ void Bridge::send_irc_user_ping_request(const std::string& irc_hostname, const s ...@@ -470,7 +470,7 @@ void Bridge::send_irc_user_ping_request(const std::string& irc_hostname, const s
const std::string& iq_id, const std::string& to_jid, const std::string& iq_id, const std::string& to_jid,
const std::string& from_jid) const std::string& from_jid)
{ {
Iid iid(nick + "!" + irc_hostname); Iid iid(nick, irc_hostname, Iid::Type::User);
this->send_private_message(iid, "\01PING " + iq_id + "\01"); this->send_private_message(iid, "\01PING " + iq_id + "\01");
irc_responder_callback_t cb = [this, nick=utils::tolower(nick), iq_id, to_jid, irc_hostname, from_jid](const std::string& hostname, const IrcMessage& message) -> bool irc_responder_callback_t cb = [this, nick=utils::tolower(nick), iq_id, to_jid, irc_hostname, from_jid](const std::string& hostname, const IrcMessage& message) -> bool
...@@ -541,7 +541,7 @@ void Bridge::send_irc_version_request(const std::string& irc_hostname, const std ...@@ -541,7 +541,7 @@ void Bridge::send_irc_version_request(const std::string& irc_hostname, const std
const std::string& iq_id, const std::string& to_jid, const std::string& iq_id, const std::string& to_jid,
const std::string& from_jid) const std::string& from_jid)
{ {
Iid iid(target + "!" + irc_hostname); Iid iid(target, irc_hostname, Iid::Type::User);
this->send_private_message(iid, "\01VERSION\01"); this->send_private_message(iid, "\01VERSION\01");
// TODO, add a timer to remove that waiting iq if the server does not // TODO, add a timer to remove that waiting iq if the server does not
// respond with a matching command before n seconds // respond with a matching command before n seconds
...@@ -590,7 +590,7 @@ void Bridge::send_message(const Iid& iid, const std::string& nick, const std::st ...@@ -590,7 +590,7 @@ void Bridge::send_message(const Iid& iid, const std::string& nick, const std::st
const auto it = this->preferred_user_from.find(iid.get_local()); const auto it = this->preferred_user_from.find(iid.get_local());
if (it != this->preferred_user_from.end()) if (it != this->preferred_user_from.end())
{ {
const auto chan_name = Iid(Jid(it->second).local).get_local(); const auto chan_name = Iid(Jid(it->second).local, {}).get_local();
for (const auto& resource: this->resources_in_chan[ChannelKey{chan_name, iid.get_server()}]) for (const auto& resource: this->resources_in_chan[ChannelKey{chan_name, iid.get_server()}])
this->xmpp.send_message(it->second, this->make_xmpp_body(body, encoding), this->xmpp.send_message(it->second, this->make_xmpp_body(body, encoding),
this->user_jid + "/" + resource, "chat", true); this->user_jid + "/" + resource, "chat", true);
...@@ -653,7 +653,7 @@ void Bridge::send_xmpp_message(const std::string& from, const std::string& autho ...@@ -653,7 +653,7 @@ void Bridge::send_xmpp_message(const std::string& from, const std::string& autho
else else
body = msg; body = msg;
const auto encoding = in_encoding_for(*this, {from}); const auto encoding = in_encoding_for(*this, {from, this});
for (const auto& resource: this->resources_in_server[from]) for (const auto& resource: this->resources_in_server[from])
{ {
this->xmpp.send_message(from, this->make_xmpp_body(body, encoding), this->user_jid + "/" + resource, "chat"); this->xmpp.send_message(from, this->make_xmpp_body(body, encoding), this->user_jid + "/" + resource, "chat");
...@@ -696,7 +696,7 @@ void Bridge::send_topic(const std::string& hostname, const std::string& chan_nam ...@@ -696,7 +696,7 @@ void Bridge::send_topic(const std::string& hostname, const std::string& chan_nam
{ {
std::string encoded_chan_name(chan_name); std::string encoded_chan_name(chan_name);
xep0106::encode(encoded_chan_name); xep0106::encode(encoded_chan_name);
const auto encoding = in_encoding_for(*this, {encoded_chan_name + '%' + hostname}); const auto encoding = in_encoding_for(*this, {encoded_chan_name, hostname, Iid::Type::Channel});
this->xmpp.send_topic(encoded_chan_name + utils::empty_if_fixed_server( this->xmpp.send_topic(encoded_chan_name + utils::empty_if_fixed_server(
"%" + hostname), this->make_xmpp_body(topic, encoding), this->user_jid + "/" + resource, who); "%" + hostname), this->make_xmpp_body(topic, encoding), this->user_jid + "/" + resource, who);
...@@ -741,7 +741,7 @@ void Bridge::send_iq_version_request(const std::string& nick, const std::string& ...@@ -741,7 +741,7 @@ void Bridge::send_iq_version_request(const std::string& nick, const std::string&
{ {
const auto resources = this->resources_in_server[hostname]; const auto resources = this->resources_in_server[hostname];
if (resources.begin() != resources.end()) if (resources.begin() != resources.end())
this->xmpp.send_iq_version_request(utils::tolower(nick) + "!" + utils::empty_if_fixed_server(hostname), this->user_jid + "/" + *resources.begin()); this->xmpp.send_iq_version_request(utils::tolower(nick) + "%" + utils::empty_if_fixed_server(hostname), this->user_jid + "/" + *resources.begin());
} }
void Bridge::send_xmpp_ping_request(const std::string& nick, const std::string& hostname, void Bridge::send_xmpp_ping_request(const std::string& nick, const std::string& hostname,
...@@ -753,7 +753,7 @@ void Bridge::send_xmpp_ping_request(const std::string& nick, const std::string& ...@@ -753,7 +753,7 @@ void Bridge::send_xmpp_ping_request(const std::string& nick, const std::string&
// Forward to the first resource (arbitrary, based on the “order” of the std::set) only // Forward to the first resource (arbitrary, based on the “order” of the std::set) only
const auto resources = this->resources_in_server[hostname]; const auto resources = this->resources_in_server[hostname];
if (resources.begin() != resources.end()) if (resources.begin() != resources.end())
this->xmpp.send_ping_request(utils::tolower(nick) + "!" + utils::empty_if_fixed_server(hostname), this->user_jid + "/" + *resources.begin(), utils::revstr(id)); this->xmpp.send_ping_request(utils::tolower(nick) + "%" + utils::empty_if_fixed_server(hostname), this->user_jid + "/" + *resources.begin(), utils::revstr(id));
} }
void Bridge::set_preferred_from_jid(const std::string& nick, const std::string& full_jid) void Bridge::set_preferred_from_jid(const std::string& nick, const std::string& full_jid)
...@@ -776,7 +776,7 @@ void Bridge::remove_all_preferred_from_jid_of_room(const std::string& channel_na ...@@ -776,7 +776,7 @@ void Bridge::remove_all_preferred_from_jid_of_room(const std::string& channel_na
{ {
for (auto it = this->preferred_user_from.begin(); it != this->preferred_user_from.end();) for (auto it = this->preferred_user_from.begin(); it != this->preferred_user_from.end();)
{ {
Iid iid(Jid(it->second).local); Iid iid(Jid(it->second).local, {});
if (iid.get_local() == channel_name) if (iid.get_local() == channel_name)
it = this->preferred_user_from.erase(it); it = this->preferred_user_from.erase(it);
else else
...@@ -806,6 +806,14 @@ std::unordered_map<std::string, std::shared_ptr<IrcClient>>& Bridge::get_irc_cli ...@@ -806,6 +806,14 @@ std::unordered_map<std::string, std::shared_ptr<IrcClient>>& Bridge::get_irc_cli
return this->irc_clients; return this->irc_clients;
} }
std::set<char> Bridge::get_chantypes(const std::string& hostname) const
{
IrcClient* irc = this->find_irc_client(hostname);
if (!irc)
return {'#', '&'};
return irc->get_chantypes();
}
void Bridge::add_resource_to_chan(const Bridge::ChannelKey& channel, const std::string& resource) void Bridge::add_resource_to_chan(const Bridge::ChannelKey& channel, const std::string& resource)
{ {
auto it = this->resources_in_chan.find(channel); auto it = this->resources_in_chan.find(channel);
......
...@@ -201,6 +201,7 @@ public: ...@@ -201,6 +201,7 @@ public:
*/ */
void trigger_on_irc_message(const std::string& irc_hostname, const IrcMessage& message); void trigger_on_irc_message(const std::string& irc_hostname, const IrcMessage& message);
std::unordered_map<std::string, std::shared_ptr<IrcClient>>& get_irc_clients(); std::unordered_map<std::string, std::shared_ptr<IrcClient>>& get_irc_clients();
std::set<char> get_chantypes(const std::string& hostname) const;
private: private:
/** /**
...@@ -217,7 +218,7 @@ private: ...@@ -217,7 +218,7 @@ private:
/** /**
* Idem, but returns nullptr if the server does not exist. * Idem, but returns nullptr if the server does not exist.
*/ */
IrcClient* find_irc_client(const std::string& hostname); IrcClient* find_irc_client(const std::string& hostname) const;
/** /**
* The bare JID of the user associated with this bridge. Messages from/to this * The bare JID of the user associated with this bridge. Messages from/to this
* JID are only managed by this bridge. * JID are only managed by this bridge.
......
#include <utils/tolower.hpp> #include <utils/tolower.hpp>
#include <config/config.hpp> #include <config/config.hpp>
#include <bridge/bridge.hpp>
#include <irc/iid.hpp> #include <irc/iid.hpp>
#include <utils/encoding.hpp> #include <utils/encoding.hpp>
Iid::Iid(const std::string& iid): Iid::Iid(const std::string local, const std::string server, Iid::Type type):
is_channel(false), type(type),
is_user(false) local(local),
server(server)
{ {
const std::string fixed_irc_server = Config::get("fixed_irc_server", "");
if (fixed_irc_server.empty())
this->init(iid);
else
this->init_with_fixed_server(iid, fixed_irc_server);
} }
Iid::Iid(const std::string& iid, const std::set<char>& chantypes)
{
this->init(iid);
this->set_type(std::set<char>(chantypes));
}
void Iid::init(const std::string& iid) Iid::Iid(const std::string& iid, const std::initializer_list<char>& chantypes):
Iid(iid, std::set<char>(chantypes))
{ {
const std::string::size_type sep = iid.find_first_of("%!");
if (sep != std::string::npos)
{
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->set_server(iid);
} }
void Iid::init_with_fixed_server(const std::string& iid, const std::string& hostname) Iid::Iid(const std::string& iid, const Bridge *bridge)
{
this->init(iid);
const auto chantypes = bridge->get_chantypes(this->server);
this->set_type(chantypes);
}
void Iid::set_type(const std::set<char>& chantypes)
{ {
this->set_server(hostname); if (this->local.empty())
return;
const std::string::size_type sep = iid.find("!"); if (chantypes.count(this->local[0]) == 1)
this->type = Iid::Type::Channel;
else
this->type = Iid::Type::User;
}
// Without any separator, we consider that it's a channel void Iid::init(const std::string& iid)
if (sep == std::string::npos) {
const std::string fixed_irc_server = Config::get("fixed_irc_server", "");
if (fixed_irc_server.empty())
{
const std::string::size_type sep = iid.find('%');
if (sep != std::string::npos)
{ {
this->is_channel = true; this->set_local(iid.substr(0, sep));
this->set_local(iid); this->set_server(iid.substr(sep + 1));
this->type = Iid::Type::Channel;
} }
else // A separator can be present to differenciate a channel from a user, else
// but the part behind it (the hostname) is ignored
{ {
this->set_local(iid.substr(0, sep)); this->set_server(iid);
this->is_user = true; this->type = Iid::Type::Server;
} }
} }
else
Iid::Iid(): {
is_channel(false), this->set_server(fixed_irc_server);
is_user(false) this->set_local(iid);
{ }
} }
void Iid::set_local(const std::string& loc) void Iid::set_local(const std::string& loc)
...@@ -88,27 +96,18 @@ const std::string& Iid::get_server() const ...@@ -88,27 +96,18 @@ const std::string& Iid::get_server() const
return this->server; return this->server;
} }
std::string Iid::get_sep() const
{
if (this->is_channel)
return "%";
else if (this->is_user)
return "!";
return "";
}
namespace std { namespace std {
const std::string to_string(const Iid& iid) const std::string to_string(const Iid& iid)
{ {
if (Config::get("fixed_irc_server", "").empty()) if (Config::get("fixed_irc_server", "").empty())
return iid.get_encoded_local() + iid.get_sep() + iid.get_server(); {
if (iid.type == Iid::Type::Server)
return iid.get_server();
else
return iid.get_encoded_local() + iid.separator + iid.get_server();
}
else else
{ return iid.get_encoded_local();
if (iid.get_sep() == "!")
return iid.get_encoded_local() + iid.get_sep();
else
return iid.get_encoded_local();
}
} }
} }
......
...@@ -2,48 +2,64 @@ ...@@ -2,48 +2,64 @@
#include <string> #include <string>
#include <set>
class Bridge;
/** /**
* A name representing an IRC channel on an IRC server, or an IRC user on an * A name representing an IRC channel on an IRC server, or an IRC user on an
* IRC server, or just an IRC server. * IRC server, or just an IRC server.
* *
* The separator for an user is '!', for a channel it's '%'. If no separator * The separator is '%' between the local part (nickname or channel) and the
* is present, it's just an irc server. * server part. If no separator is present, it's just an irc server.
* If it is present, the first character of the local part determines if it’s
* a channel or a user: ff the local part is empty or if its first character
* is part of the chantypes characters, then it’s a channel, otherwise it’s
* a user.
*
* It’s possible to have an empty-string server, but it makes no sense in * It’s possible to have an empty-string server, but it makes no sense in
* the biboumi context. * biboumi’s context.
*
* Assuming the chantypes are '#' and '&':
* *
* #test%irc.example.org has : * #test%irc.example.org has :
* - local: "#test" (the # is part of the name, it could very well be absent, or & (for example) instead) * - local: "#test" (the # is part of the name, it could very well be absent, or & (for example) instead)
* - server: "irc.example.org" * - server: "irc.example.org"
* - is_channel: true * - type: channel
* - is_user: false
* *
* %irc.example.org: * %irc.example.org:
* - local: "" * - local: ""
* - server: "irc.example.org" * - server: "irc.example.org"
* - is_channel: true * - type: channel
* - is_user: false * Note: this is the special empty-string channel, used internally in biboumi
* Note: this is the special empty-string channel, used internal in biboumi
* but has no meaning on IRC. * but has no meaning on IRC.
* *
* foo!irc.example.org * foo%irc.example.org
* - local: "foo" * - local: "foo"
* - server: "irc.example.org" * - server: "irc.example.org"
* - is_channel: false * - type: user
* - is_user: true * Note: the empty-string user (!irc.example.org) makes no sense for biboumi
* Note: the empty-string user (!irc.example.org) has no special meaning in biboumi
* *
* irc.example.org: * irc.example.org:
* - local: "" * - local: ""
* - server: "irc.example.org" * - server: "irc.example.org"
* - is_channel: false * - type: server
* - is_user: false
*/ */
class Iid class Iid
{ {
public: public:
Iid(const std::string& iid); enum class Type
Iid(); {
Channel,
User,
Server,
};
static constexpr auto separator = "%";
Iid(const std::string& iid, const std::set<char>& chantypes);
Iid(const std::string& iid, const std::initializer_list<char>& chantypes);
Iid(const std::string& iid, const Bridge* bridge);
Iid(const std::string local, const std::string server, Type type);
Iid() = default;
Iid(const Iid&) = default; Iid(const Iid&) = default;
Iid(Iid&&) = delete; Iid(Iid&&) = delete;
...@@ -52,21 +68,19 @@ public: ...@@ -52,21 +68,19 @@ public:
void set_local(const std::string& loc); void set_local(const std::string& loc);
void set_server(const std::string& serv); void set_server(const std::string& serv);
const std::string& get_local() const; const std::string& get_local() const;
const std::string get_encoded_local() const; const std::string get_encoded_local() const;
const std::string& get_server() const; const std::string& get_server() const;
bool is_channel;
bool is_user;
std::string get_sep() const;
std::tuple<std::string, std::string> to_tuple() const; std::tuple<std::string, std::string> to_tuple() const;
Type type { Type::Server };
private: private:
void init(const std::string& iid); void init(const std::string& iid);
void init_with_fixed_server(const std::string& iid, const std::string& hostname); void set_type(const std::set<char>& chantypes);
std::string local; std::string local;
std::string server; std::string server;
......
...@@ -213,7 +213,7 @@ void IrcClient::on_connection_failed(const std::string& reason) ...@@ -213,7 +213,7 @@ void IrcClient::on_connection_failed(const std::string& reason)
// Send an error message for all room that the user wanted to join // Send an error message for all room that the user wanted to join
for (const auto& tuple: this->channels_to_join) for (const auto& tuple: this->channels_to_join)
{ {
Iid iid(std::get<0>(tuple) + "%" + this->hostname); Iid iid(std::get<0>(tuple) + "%" + this->hostname, this->chantypes);
this->bridge.send_presence_error(iid, this->current_nick, this->bridge.send_presence_error(iid, this->current_nick,
"cancel", "item-not-found", "cancel", "item-not-found",
"", reason); "", reason);
...@@ -551,7 +551,7 @@ void IrcClient::on_notice(const IrcMessage& message) ...@@ -551,7 +551,7 @@ void IrcClient::on_notice(const IrcMessage& message)
if (this->nicks_to_treat_as_private.find(nick) != if (this->nicks_to_treat_as_private.find(nick) !=
this->nicks_to_treat_as_private.end()) this->nicks_to_treat_as_private.end())
{ // We previously sent a message to that nick) { // We previously sent a message to that nick)
this->bridge.send_message({nick + "!" + this->hostname}, nick, body, this->bridge.send_message({nick, this->hostname, Iid::Type::User}, nick, body,
false); false);
} }
else else
...@@ -663,12 +663,12 @@ void IrcClient::on_channel_message(const IrcMessage& message) ...@@ -663,12 +663,12 @@ void IrcClient::on_channel_message(const IrcMessage& message)
bool muc = true; bool muc = true;
if (!this->get_channel(iid.get_local())->joined) if (!this->get_channel(iid.get_local())->joined)
{ {
iid.is_user = true; iid.type = Iid::Type::User;
iid.set_local(nick); iid.set_local(nick);
muc = false; muc = false;
} }
else else
iid.is_channel = true; iid.type = Iid::Type::Channel;
if (!body.empty() && body[0] == '\01') if (!body.empty() && body[0] == '\01')
{ {
if (body.substr(1, 6) == "ACTION") if (body.substr(1, 6) == "ACTION")
...@@ -780,7 +780,7 @@ void IrcClient::on_nickname_conflict(const IrcMessage& message) ...@@ -780,7 +780,7 @@ void IrcClient::on_nickname_conflict(const IrcMessage& message)
Iid iid; Iid iid;
iid.set_local(it->first); iid.set_local(it->first);
iid.set_server(this->hostname); iid.set_server(this->hostname);
iid.is_channel = true; iid.type = Iid::Type::Channel;
this->bridge.send_nickname_conflict_error(iid, nickname); this->bridge.send_nickname_conflict_error(iid, nickname);
} }
} }
...@@ -797,7 +797,7 @@ void IrcClient::on_nickname_change_too_fast(const IrcMessage& message) ...@@ -797,7 +797,7 @@ void IrcClient::on_nickname_change_too_fast(const IrcMessage& message)
Iid iid; Iid iid;
iid.set_local(it->first); iid.set_local(it->first);
iid.set_server(this->hostname); iid.set_server(this->hostname);
iid.is_channel = true; iid.type = Iid::Type::Channel;
this->bridge.send_presence_error(iid, nickname, this->bridge.send_presence_error(iid, nickname,
"cancel", "not-acceptable", "cancel", "not-acceptable",
"", txt); "", txt);
...@@ -858,7 +858,7 @@ void IrcClient::on_part(const IrcMessage& message) ...@@ -858,7 +858,7 @@ void IrcClient::on_part(const IrcMessage& message)
Iid iid; Iid iid;
iid.set_local(chan_name); iid.set_local(chan_name);
iid.set_server(this->hostname); iid.set_server(this->hostname);
iid.is_channel = true; iid.type = Iid::Type::Channel;
bool self = channel->get_self()->nick == nick; bool self = channel->get_self()->nick == nick;
if (self) if (self)
{ {
...@@ -880,7 +880,7 @@ void IrcClient::on_error(const IrcMessage& message) ...@@ -880,7 +880,7 @@ void IrcClient::on_error(const IrcMessage& message)
Iid iid; Iid iid;
iid.set_local(it->first); iid.set_local(it->first);
iid.set_server(this->hostname); iid.set_server(this->hostname);
iid.is_channel = true; iid.type = Iid::Type::Channel;
IrcChannel* channel = it->second.get(); IrcChannel* channel = it->second.get();
if (!channel->joined) if (!channel->joined)
continue; continue;
...@@ -908,7 +908,7 @@ void IrcClient::on_quit(const IrcMessage& message) ...@@ -908,7 +908,7 @@ void IrcClient::on_quit(const IrcMessage& message)
Iid iid; Iid iid;
iid.set_local(chan_name); iid.set_local(chan_name);
iid.set_server(this->hostname); iid.set_server(this->hostname);
iid.is_channel = true; iid.type = Iid::Type::Channel;
this->bridge.send_muc_leave(std::move(iid), std::move(nick), txt, false); this->bridge.send_muc_leave(std::move(iid), std::move(nick), txt, false);
} }
} }
...@@ -928,7 +928,7 @@ void IrcClient::on_nick(const IrcMessage& message) ...@@ -928,7 +928,7 @@ void IrcClient::on_nick(const IrcMessage& message)
Iid iid; Iid iid;
iid.set_local(chan_name); iid.set_local(chan_name);
iid.set_server(this->hostname); iid.set_server(this->hostname);
iid.is_channel = true; iid.type = Iid::Type::Channel;
const bool self = channel->get_self()->nick == old_nick; const bool self = channel->get_self()->nick == old_nick;
const char user_mode = user->get_most_significant_mode(this->sorted_user_modes); 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); this->bridge.send_nick_change(std::move(iid), old_nick, new_nick, user_mode, self);
...@@ -956,7 +956,7 @@ void IrcClient::on_kick(const IrcMessage& message) ...@@ -956,7 +956,7 @@ void IrcClient::on_kick(const IrcMessage& message)
Iid iid; Iid iid;
iid.set_local(chan_name); iid.set_local(chan_name);
iid.set_server(this->hostname); iid.set_server(this->hostname);
iid.is_channel = true; iid.type = Iid::Type::Channel;
this->bridge.kick_muc_user(std::move(iid), target, reason, author.nick); this->bridge.kick_muc_user(std::move(iid), target, reason, author.nick);
} }
...@@ -976,7 +976,7 @@ void IrcClient::on_channel_mode(const IrcMessage& message) ...@@ -976,7 +976,7 @@ void IrcClient::on_channel_mode(const IrcMessage& message)
Iid iid; Iid iid;
iid.set_local(message.arguments[0]); iid.set_local(message.arguments[0]);
iid.set_server(this->hostname); iid.set_server(this->hostname);
iid.is_channel = true; iid.type = Iid::Type::Channel;
IrcUser user(message.prefix); IrcUser user(message.prefix);