diff options
-rw-r--r-- | src/bridge/bridge.cpp | 26 | ||||
-rw-r--r-- | src/bridge/bridge.hpp | 3 | ||||
-rw-r--r-- | src/irc/iid.cpp | 109 | ||||
-rw-r--r-- | src/irc/iid.hpp | 58 | ||||
-rw-r--r-- | src/irc/irc_client.cpp | 26 | ||||
-rw-r--r-- | src/irc/irc_client.hpp | 3 | ||||
-rw-r--r-- | src/xmpp/biboumi_adhoc_commands.cpp | 4 | ||||
-rw-r--r-- | src/xmpp/biboumi_component.cpp | 61 | ||||
-rw-r--r-- | tests/end_to_end/__main__.py | 44 | ||||
-rw-r--r-- | tests/iid.cpp | 64 |
10 files changed, 207 insertions, 191 deletions
diff --git a/src/bridge/bridge.cpp b/src/bridge/bridge.cpp index 17d3ec6..ac61dbc 100644 --- a/src/bridge/bridge.cpp +++ b/src/bridge/bridge.cpp @@ -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 { @@ -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& from_jid) { - Iid iid(nick + "!" + irc_hostname); + Iid iid(nick, irc_hostname, Iid::Type::User); 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 @@ -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& from_jid) { - Iid iid(target + "!" + irc_hostname); + Iid iid(target, irc_hostname, Iid::Type::User); this->send_private_message(iid, "\01VERSION\01"); // TODO, add a timer to remove that waiting iq if the server does not // 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 const auto it = this->preferred_user_from.find(iid.get_local()); 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()}]) this->xmpp.send_message(it->second, this->make_xmpp_body(body, encoding), this->user_jid + "/" + resource, "chat", true); @@ -653,7 +653,7 @@ void Bridge::send_xmpp_message(const std::string& from, const std::string& autho else 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]) { 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 { std::string encoded_chan_name(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( "%" + 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& { const auto resources = this->resources_in_server[hostname]; 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, @@ -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 const auto resources = this->resources_in_server[hostname]; 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) @@ -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();) { - Iid iid(Jid(it->second).local); + Iid iid(Jid(it->second).local, {}); if (iid.get_local() == channel_name) it = this->preferred_user_from.erase(it); else @@ -806,6 +806,14 @@ std::unordered_map<std::string, std::shared_ptr<IrcClient>>& Bridge::get_irc_cli 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) { auto it = this->resources_in_chan.find(channel); diff --git a/src/bridge/bridge.hpp b/src/bridge/bridge.hpp index 69b7bd5..d7b2a5c 100644 --- a/src/bridge/bridge.hpp +++ b/src/bridge/bridge.hpp @@ -201,6 +201,7 @@ public: */ 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::set<char> get_chantypes(const std::string& hostname) const; private: /** @@ -217,7 +218,7 @@ private: /** * 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 * JID are only managed by this bridge. diff --git a/src/irc/iid.cpp b/src/irc/iid.cpp index 0e2841e..c951a49 100644 --- a/src/irc/iid.cpp +++ b/src/irc/iid.cpp @@ -1,62 +1,70 @@ #include <utils/tolower.hpp> #include <config/config.hpp> - +#include <bridge/bridge.hpp> #include <irc/iid.hpp> #include <utils/encoding.hpp> -Iid::Iid(const std::string& iid): - is_channel(false), - is_user(false) +Iid::Iid(const std::string local, const std::string server, Iid::Type type): + type(type), + 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 - if (sep == std::string::npos) +void Iid::init(const std::string& iid) +{ + 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); + this->set_local(iid.substr(0, sep)); + 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, - // but the part behind it (the hostname) is ignored + else { - this->set_local(iid.substr(0, sep)); - this->is_user = true; + this->set_server(iid); + this->type = Iid::Type::Server; } -} - -Iid::Iid(): - is_channel(false), - is_user(false) -{ + } + else + { + this->set_server(fixed_irc_server); + this->set_local(iid); + } } void Iid::set_local(const std::string& loc) @@ -88,27 +96,18 @@ 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) { 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 - { - if (iid.get_sep() == "!") - return iid.get_encoded_local() + iid.get_sep(); - else - return iid.get_encoded_local(); - } + return iid.get_encoded_local(); } } diff --git a/src/irc/iid.hpp b/src/irc/iid.hpp index 3b11470..7361c51 100644 --- a/src/irc/iid.hpp +++ b/src/irc/iid.hpp @@ -2,48 +2,64 @@ #include <string> +#include <set> + +class Bridge; /** * 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. + * The separator is '%' between the local part (nickname or channel) and the + * 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 - * the biboumi context. + * biboumi’s context. + * + * Assuming the chantypes are '#' and '&': * * #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 + * - type: channel * * %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 + * - type: channel + * Note: this is the special empty-string channel, used internally in biboumi * but has no meaning on IRC. * - * foo!irc.example.org + * 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 + * - type: user + * Note: the empty-string user (!irc.example.org) makes no sense for biboumi * * irc.example.org: * - local: "" * - server: "irc.example.org" - * - is_channel: false - * - is_user: false + * - type: server */ class Iid { public: - Iid(const std::string& iid); - Iid(); + enum class Type + { + 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(Iid&&) = delete; @@ -52,21 +68,19 @@ public: void set_local(const std::string& loc); void set_server(const std::string& serv); + const std::string& get_local() const; const std::string get_encoded_local() 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; + Type type { Type::Server }; + private: 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 server; diff --git a/src/irc/irc_client.cpp b/src/irc/irc_client.cpp index dd83307..d00b6ee 100644 --- a/src/irc/irc_client.cpp +++ b/src/irc/irc_client.cpp @@ -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 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, "cancel", "item-not-found", "", reason); @@ -551,7 +551,7 @@ void IrcClient::on_notice(const IrcMessage& message) if (this->nicks_to_treat_as_private.find(nick) != this->nicks_to_treat_as_private.end()) { // 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); } else @@ -663,12 +663,12 @@ void IrcClient::on_channel_message(const IrcMessage& message) bool muc = true; if (!this->get_channel(iid.get_local())->joined) { - iid.is_user = true; + iid.type = Iid::Type::User; iid.set_local(nick); muc = false; } else - iid.is_channel = true; + iid.type = Iid::Type::Channel; if (!body.empty() && body[0] == '\01') { if (body.substr(1, 6) == "ACTION") @@ -780,7 +780,7 @@ void IrcClient::on_nickname_conflict(const IrcMessage& message) Iid iid; iid.set_local(it->first); iid.set_server(this->hostname); - iid.is_channel = true; + iid.type = Iid::Type::Channel; this->bridge.send_nickname_conflict_error(iid, nickname); } } @@ -797,7 +797,7 @@ void IrcClient::on_nickname_change_too_fast(const IrcMessage& message) Iid iid; iid.set_local(it->first); iid.set_server(this->hostname); - iid.is_channel = true; + iid.type = Iid::Type::Channel; this->bridge.send_presence_error(iid, nickname, "cancel", "not-acceptable", "", txt); @@ -858,7 +858,7 @@ void IrcClient::on_part(const IrcMessage& message) Iid iid; iid.set_local(chan_name); iid.set_server(this->hostname); - iid.is_channel = true; + iid.type = Iid::Type::Channel; bool self = channel->get_self()->nick == nick; if (self) { @@ -880,7 +880,7 @@ void IrcClient::on_error(const IrcMessage& message) Iid iid; iid.set_local(it->first); iid.set_server(this->hostname); - iid.is_channel = true; + iid.type = Iid::Type::Channel; IrcChannel* channel = it->second.get(); if (!channel->joined) continue; @@ -908,7 +908,7 @@ void IrcClient::on_quit(const IrcMessage& message) Iid iid; iid.set_local(chan_name); 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); } } @@ -928,7 +928,7 @@ void IrcClient::on_nick(const IrcMessage& message) Iid iid; iid.set_local(chan_name); iid.set_server(this->hostname); - iid.is_channel = true; + iid.type = Iid::Type::Channel; 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); @@ -956,7 +956,7 @@ void IrcClient::on_kick(const IrcMessage& message) Iid iid; iid.set_local(chan_name); 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); } @@ -976,7 +976,7 @@ void IrcClient::on_channel_mode(const IrcMessage& message) Iid iid; iid.set_local(message.arguments[0]); iid.set_server(this->hostname); - iid.is_channel = true; + iid.type = Iid::Type::Channel; IrcUser user(message.prefix); std::string mode_arguments; for (size_t i = 1; i < message.arguments.size(); ++i) @@ -1105,7 +1105,7 @@ void IrcClient::leave_dummy_channel(const std::string& exit_message) this->dummy_channel.joined = false; this->dummy_channel.joining = false; this->dummy_channel.remove_all_users(); - this->bridge.send_muc_leave(Iid("%"s + this->hostname), std::string(this->current_nick), exit_message, true); + this->bridge.send_muc_leave(Iid("%"s + this->hostname, this->chantypes), std::string(this->current_nick), exit_message, true); } #ifdef BOTAN_FOUND diff --git a/src/irc/irc_client.hpp b/src/irc/irc_client.hpp index fc3918e..fa35e65 100644 --- a/src/irc/irc_client.hpp +++ b/src/irc/irc_client.hpp @@ -280,8 +280,9 @@ public: const Resolver& get_resolver() const { return this->dns_resolver; } - const std::vector<char>& get_sorted_user_modes() const { return sorted_user_modes; } + const std::vector<char>& get_sorted_user_modes() const { return this->sorted_user_modes; } + std::set<char> get_chantypes() const { return this->chantypes; } private: /** * The hostname of the server we are connected to. diff --git a/src/xmpp/biboumi_adhoc_commands.cpp b/src/xmpp/biboumi_adhoc_commands.cpp index eec930d..b14081f 100644 --- a/src/xmpp/biboumi_adhoc_commands.cpp +++ b/src/xmpp/biboumi_adhoc_commands.cpp @@ -381,7 +381,7 @@ void ConfigureIrcChannelStep1(XmppComponent&, AdhocSession& session, XmlNode& co { const Jid owner(session.get_owner_jid()); const Jid target(session.get_target_jid()); - const Iid iid(target.local); + const Iid iid(target.local, {}); auto options = Database::get_irc_channel_options_with_server_default(owner.local + "@" + owner.domain, iid.get_server(), iid.get_local()); @@ -434,7 +434,7 @@ void ConfigureIrcChannelStep2(XmppComponent&, AdhocSession& session, XmlNode& co { const Jid owner(session.get_owner_jid()); const Jid target(session.get_target_jid()); - const Iid iid(target.local); + const Iid iid(target.local, {}); auto options = Database::get_irc_channel_options(owner.local + "@" + owner.domain, iid.get_server(), iid.get_local()); for (const XmlNode* field: x->get_children("field", "jabber:x:data")) diff --git a/src/xmpp/biboumi_component.cpp b/src/xmpp/biboumi_component.cpp index a6aac21..f621a6d 100644 --- a/src/xmpp/biboumi_component.cpp +++ b/src/xmpp/biboumi_component.cpp @@ -126,7 +126,7 @@ void BiboumiComponent::handle_presence(const Stanza& stanza) Bridge* bridge = this->get_user_bridge(from_str); Jid to(to_str); Jid from(from_str); - Iid iid(to.local); + Iid iid(to.local, bridge); // An error stanza is sent whenever we exit this function without // disabling this scopeguard. If error_type and error_name are not @@ -142,7 +142,7 @@ void BiboumiComponent::handle_presence(const Stanza& stanza) }); try { - if (iid.is_channel && !iid.get_server().empty()) + 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 if (type.empty()) @@ -191,7 +191,7 @@ void BiboumiComponent::handle_message(const Stanza& stanza) type = "normal"; Bridge* bridge = this->get_user_bridge(from); Jid to(to_str); - Iid iid(to.local); + Iid iid(to.local, bridge); std::string error_type("cancel"); std::string error_name("internal-server-error"); @@ -202,7 +202,7 @@ void BiboumiComponent::handle_message(const Stanza& stanza) const XmlNode* body = stanza.get_child("body", COMPONENT_NS); try { // catch IRCNotConnected exceptions - if (type == "groupchat" && iid.is_channel) + if (type == "groupchat" && iid.type == Iid::Type::Channel) { if (body && !body->get_inner().empty()) { @@ -234,27 +234,27 @@ void BiboumiComponent::handle_message(const Stanza& stanza) if (body && !body->get_inner().empty()) { // a message for nick!server - if (iid.is_user && !iid.get_local().empty()) + if (iid.type == Iid::Type::User && !iid.get_local().empty()) { bridge->send_private_message(iid, body->get_inner()); bridge->remove_preferred_from_jid(iid.get_local()); } - else if (!iid.is_user && !to.resource.empty()) + else if (iid.type != Iid::Type::User && !to.resource.empty()) { // a message for chan%server@biboumi/Nick or // server@biboumi/Nick // Convert that into a message to nick!server - Iid user_iid(utils::tolower(to.resource) + "!" + iid.get_server()); + Iid user_iid(utils::tolower(to.resource), iid.get_server(), Iid::Type::User); bridge->send_private_message(user_iid, body->get_inner()); bridge->set_preferred_from_jid(user_iid.get_local(), to_str); } - else if (!iid.is_user && !iid.is_channel) + else if (iid.type == Iid::Type::Server) { // Message sent to the server JID // Convert the message body into a raw IRC message bridge->send_raw_message(iid.get_server(), body->get_inner()); } } } - else if (iid.is_user) + else if (iid.type == Iid::Type::User) this->send_invalid_user_error(to.local, from); } catch (const IRCNotConnected& ex) { @@ -321,7 +321,7 @@ void BiboumiComponent::handle_iq(const Stanza& stanza) std::string affiliation = child->get_tag("affiliation"); if (!nick.empty()) { - Iid iid(to.local); + Iid iid(to.local, {}); if (role == "none") { // This is a kick std::string reason; @@ -345,15 +345,17 @@ void BiboumiComponent::handle_iq(const Stanza& stanza) // Depending on the 'to' jid in the request, we use one adhoc // command handler or an other - Iid iid(to.local); + Iid iid(to.local, {}); AdhocCommandsHandler* adhoc_handler; - if (!to.local.empty() && !iid.is_user && !iid.is_channel) - adhoc_handler = &this->irc_server_adhoc_commands_handler; - else if (!to.local.empty() && iid.is_channel) - adhoc_handler = &this->irc_channel_adhoc_commands_handler; - else + if (to.local.empty()) adhoc_handler = &this->adhoc_commands_handler; - + else + { + if (iid.type == Iid::Type::Server) + adhoc_handler = &this->irc_server_adhoc_commands_handler; + else + adhoc_handler = &this->irc_channel_adhoc_commands_handler; + } // Execute the command, if any, and get a result XmlNode that we // insert in our response XmlNode inner_node = adhoc_handler->handle_request(from, to_str, *query); @@ -384,13 +386,12 @@ void BiboumiComponent::handle_iq(const Stanza& stanza) } else if ((query = stanza.get_child("query", VERSION_NS))) { - Iid iid(to.local); - if (iid.is_user || - (iid.is_channel && !to.resource.empty())) + Iid iid(to.local, bridge); + if (iid.type != Iid::Type::Server && !to.resource.empty()) { // Get the IRC user version std::string target; - if (iid.is_user) + if (iid.type == Iid::Type::User) target = iid.get_local(); else target = to.resource; @@ -406,7 +407,7 @@ void BiboumiComponent::handle_iq(const Stanza& stanza) } else if ((query = stanza.get_child("query", DISCO_ITEMS_NS))) { - Iid iid(to.local); + Iid iid(to.local, bridge); const std::string node = query->get_tag("node"); if (node == ADHOC_NS) { @@ -419,7 +420,7 @@ void BiboumiComponent::handle_iq(const Stanza& stanza) this->adhoc_commands_handler); stanza_error.disable(); } - else if (!iid.is_user && !iid.is_channel) + 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", "") == @@ -427,7 +428,7 @@ void BiboumiComponent::handle_iq(const Stanza& stanza) this->irc_server_adhoc_commands_handler); stanza_error.disable(); } - else if (!iid.is_user && iid.is_channel) + else if (iid.type == Iid::Type::Channel) { // Get the channel's adhoc commands this->send_adhoc_commands_list(id, from, to_str, (Config::get("admin", "") == @@ -436,7 +437,7 @@ void BiboumiComponent::handle_iq(const Stanza& stanza) stanza_error.disable(); } } - else if (node.empty() && !iid.is_user && !iid.is_channel) + else if (node.empty() && iid.type == Iid::Type::Server) { // Disco on an IRC server: get the list of channels bridge->send_irc_channel_list_request(iid, id, from); stanza_error.disable(); @@ -444,13 +445,13 @@ void BiboumiComponent::handle_iq(const Stanza& stanza) } else if ((query = stanza.get_child("ping", PING_NS))) { - Iid iid(to.local); - if (iid.is_user) + Iid iid(to.local, bridge); + if (iid.type == Iid::Type::User) { // Ping any user (no check on the nick done ourself) bridge->send_irc_user_ping_request(iid.get_server(), iid.get_local(), id, from, to_str); } - else if (iid.is_channel && !to.resource.empty()) + else if (iid.type == Iid::Type::Channel && !to.resource.empty()) { // Ping a room participant (we check if the nick is in the room) bridge->send_irc_participant_ping_request(iid, to.resource, id, from, to_str); @@ -481,7 +482,7 @@ void BiboumiComponent::handle_iq(const Stanza& stanza) version = version_node->get_inner(); if (os_node) os = os_node->get_inner(); - const Iid iid(to.local); + const Iid iid(to.local, bridge); bridge->send_xmpp_version_to_irc(iid, name, version, os); } else @@ -604,7 +605,7 @@ void BiboumiComponent::send_ping_request(const std::string& from, "the response mismatches the 'from' of the request"); } else - bridge->send_irc_ping_result(from, id); + bridge->send_irc_ping_result({from, bridge}, id); }; this->waiting_iq[id] = result_cb; } diff --git a/tests/end_to_end/__main__.py b/tests/end_to_end/__main__.py index 4348197..d438265 100644 --- a/tests/end_to_end/__main__.py +++ b/tests/end_to_end/__main__.py @@ -648,8 +648,8 @@ if __name__ == '__main__': # That second user sends a private message to the first one partial(send_stanza, "<message from='{jid_two}/{resource_one}' to='#foo%{irc_server_one}/{nick_one}' type='chat'><body>RELLO</body></message>"), # Message is received with a server-wide JID, by the two resources behind nick_one - partial(expect_stanza, "/message[@from='{lower_nick_two}!{irc_server_one}'][@to='{jid_one}/{resource_one}'][@type='chat']/body[text()='RELLO']"), - partial(expect_stanza, "/message[@from='{lower_nick_two}!{irc_server_one}'][@to='{jid_one}/{resource_two}'][@type='chat']/body[text()='RELLO']"), + partial(expect_stanza, "/message[@from='{lower_nick_two}%{irc_server_one}'][@to='{jid_one}/{resource_one}'][@type='chat']/body[text()='RELLO']"), + partial(expect_stanza, "/message[@from='{lower_nick_two}%{irc_server_one}'][@to='{jid_one}/{resource_two}'][@type='chat']/body[text()='RELLO']"), # One resource leaves the server entirely. partial(send_stanza, "<presence type='unavailable' from='{jid_one}/{resource_two}' to='#foo%{irc_server_one}/{nick_one}' />"), @@ -662,8 +662,8 @@ if __name__ == '__main__': partial(send_stanza, "<message from='{jid_two}/{resource_one}' to='#foo%{irc_server_one}/{nick_one}' type='chat'><body>first</body></message>"), partial(send_stanza, "<message from='{jid_two}/{resource_one}' to='#foo%{irc_server_one}/{nick_one}' type='chat'><body>second</body></message>"), # The first user receives the two messages, on the connected resource, once each - partial(expect_stanza, "/message[@from='{lower_nick_two}!{irc_server_one}'][@to='{jid_one}/{resource_one}'][@type='chat']/body[text()='first']"), - partial(expect_stanza, "/message[@from='{lower_nick_two}!{irc_server_one}'][@to='{jid_one}/{resource_one}'][@type='chat']/body[text()='second']"), + partial(expect_stanza, "/message[@from='{lower_nick_two}%{irc_server_one}'][@to='{jid_one}/{resource_one}'][@type='chat']/body[text()='first']"), + partial(expect_stanza, "/message[@from='{lower_nick_two}%{irc_server_one}'][@to='{jid_one}/{resource_one}'][@type='chat']/body[text()='second']"), ]), @@ -708,10 +708,10 @@ if __name__ == '__main__': # Send a private message, to a in-room JID partial(send_stanza, "<message from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}/{nick_two}' type='chat'><body>coucou in private</body></message>"), # Message is received with a server-wide JID - partial(expect_stanza, "/message[@from='{lower_nick_one}!{irc_server_one}'][@to='{jid_two}/{resource_one}'][@type='chat']/body[text()='coucou in private']"), + partial(expect_stanza, "/message[@from='{lower_nick_one}%{irc_server_one}'][@to='{jid_two}/{resource_one}'][@type='chat']/body[text()='coucou in private']"), # Respond to the message, to the server-wide JID - partial(send_stanza, "<message from='{jid_two}/{resource_one}' to='{lower_nick_one}!{irc_server_one}' type='chat'><body>yes</body></message>"), + partial(send_stanza, "<message from='{jid_two}/{resource_one}' to='{lower_nick_one}%{irc_server_one}' type='chat'><body>yes</body></message>"), # The response is received from the in-room JID partial(expect_stanza, "/message[@from='#foo%{irc_server_one}/{nick_two}'][@to='{jid_one}/{resource_one}'][@type='chat']/body[text()='yes']"), @@ -729,10 +729,10 @@ if __name__ == '__main__': # Send a private message, to a in-room JID partial(send_stanza, "<message from='{jid_one}/{resource_one}' to='%{irc_server_one}/{nick_two}' type='chat'><body>re in private</body></message>"), # Message is received with a server-wide JID - partial(expect_stanza, "/message[@from='{lower_nick_one}!{irc_server_one}'][@to='{jid_two}/{resource_one}'][@type='chat']/body[text()='re in private']"), + partial(expect_stanza, "/message[@from='{lower_nick_one}%{irc_server_one}'][@to='{jid_two}/{resource_one}'][@type='chat']/body[text()='re in private']"), # Respond to the message, to the server-wide JID - partial(send_stanza, "<message from='{jid_two}/{resource_one}' to='{lower_nick_one}!{irc_server_one}' type='chat'><body>re</body></message>"), + partial(send_stanza, "<message from='{jid_two}/{resource_one}' to='{lower_nick_one}%{irc_server_one}' type='chat'><body>re</body></message>"), # The response is received from the in-room JID partial(expect_stanza, "/message[@from='%{irc_server_one}/{nick_two}'][@to='{jid_one}/{resource_one}'][@type='chat']/body[text()='re']"), @@ -743,9 +743,9 @@ if __name__ == '__main__': "/presence[@type='unavailable']/muc_user:x/muc_user:status[@code='110']"), # The private messages from this nick should now come (again) from the server-wide JID - partial(send_stanza, "<message from='{jid_two}/{resource_one}' to='{lower_nick_one}!{irc_server_one}' type='chat'><body>hihihoho</body></message>"), + partial(send_stanza, "<message from='{jid_two}/{resource_one}' to='{lower_nick_one}%{irc_server_one}' type='chat'><body>hihihoho</body></message>"), partial(expect_stanza, - "/message[@from='{lower_nick_two}!{irc_server_one}'][@to='{jid_one}/{resource_one}']"), + "/message[@from='{lower_nick_two}%{irc_server_one}'][@to='{jid_one}/{resource_one}']"), ] ), Scenario("encoded_channel_join", @@ -781,10 +781,10 @@ if __name__ == '__main__': "<iq type='get' from='{jid_one}/{resource_one}' id='first_ping' to='#foo%{irc_server_one}/{nick_one}'><ping xmlns='urn:xmpp:ping' /></iq>"), # We receive our own ping request, partial(expect_stanza, - "/iq[@from='{lower_nick_one}!{irc_server_one}'][@type='get'][@to='{jid_one}/{resource_one}'][@id='gnip_tsrif']"), + "/iq[@from='{lower_nick_one}%{irc_server_one}'][@type='get'][@to='{jid_one}/{resource_one}'][@id='gnip_tsrif']"), # Respond to the request partial(send_stanza, - "<iq type='result' to='{lower_nick_one}!{irc_server_one}' id='gnip_tsrif' from='{jid_one}/{resource_one}'/>"), + "<iq type='result' to='{lower_nick_one}%{irc_server_one}' id='gnip_tsrif' from='{jid_one}/{resource_one}'/>"), partial(expect_stanza, "/iq[@from='#foo%{irc_server_one}/{nick_one}'][@type='result'][@to='{jid_one}/{resource_one}'][@id='first_ping']"), @@ -802,21 +802,21 @@ if __name__ == '__main__': "<iq type='get' from='{jid_one}/{resource_one}' id='second_ping' to='#foo%{irc_server_one}/{nick_one}'><ping xmlns='urn:xmpp:ping' /></iq>"), # We receive our own ping request. Note that we don't know the to value, it could be one of our two resources. partial(expect_stanza, - "/iq[@from='{lower_nick_one}!{irc_server_one}'][@type='get'][@to][@id='gnip_dnoces']", + "/iq[@from='{lower_nick_one}%{irc_server_one}'][@type='get'][@to][@id='gnip_dnoces']", after = partial(save_value, "to", partial(extract_attribute, "/iq", "to"))), # Respond to the request, using the extracted 'to' value as our 'from' partial(send_stanza, - "<iq type='result' to='{lower_nick_one}!{irc_server_one}' id='gnip_dnoces' from='{to}'/>"), + "<iq type='result' to='{lower_nick_one}%{irc_server_one}' id='gnip_dnoces' from='{to}'/>"), partial(expect_stanza, "/iq[@from='#foo%{irc_server_one}/{nick_one}'][@type='result'][@to='{jid_one}/{resource_one}'][@id='second_ping']"), ## And re-do exactly the same thing, just change the resource initiating the self ping partial(send_stanza, "<iq type='get' from='{jid_one}/{resource_two}' id='third_ping' to='#foo%{irc_server_one}/{nick_one}'><ping xmlns='urn:xmpp:ping' /></iq>"), partial(expect_stanza, - "/iq[@from='{lower_nick_one}!{irc_server_one}'][@type='get'][@to][@id='gnip_driht']", + "/iq[@from='{lower_nick_one}%{irc_server_one}'][@type='get'][@to][@id='gnip_driht']", after = partial(save_value, "to", partial(extract_attribute, "/iq", "to"))), partial(send_stanza, - "<iq type='result' to='{lower_nick_one}!{irc_server_one}' id='gnip_driht' from='{to}'/>"), + "<iq type='result' to='{lower_nick_one}%{irc_server_one}' id='gnip_driht' from='{to}'/>"), partial(expect_stanza, "/iq[@from='#foo%{irc_server_one}/{nick_one}'][@type='result'][@to='{jid_one}/{resource_two}'][@id='third_ping']"), @@ -944,11 +944,11 @@ if __name__ == '__main__': "<iq type='get' from='{jid_one}/{resource_one}' id='first_version' to='#foo%{irc_server_one}/{nick_one}'><query xmlns='jabber:iq:version' /></iq>"), # We receive our own request, partial(expect_stanza, - "/iq[@from='{lower_nick_one}!{irc_server_one}'][@type='get'][@to='{jid_one}/{resource_one}']", + "/iq[@from='{lower_nick_one}%{irc_server_one}'][@type='get'][@to='{jid_one}/{resource_one}']", after = partial(save_value, "id", partial(extract_attribute, "/iq", 'id'))), # Respond to the request partial(send_stanza, - "<iq type='result' to='{lower_nick_one}!{irc_server_one}' id='{id}' from='{jid_one}/{resource_one}'><query xmlns='jabber:iq:version'><name>e2e test</name><version>1.0</version><os>Fedora</os></query></iq>"), + "<iq type='result' to='{lower_nick_one}%{irc_server_one}' id='{id}' from='{jid_one}/{resource_one}'><query xmlns='jabber:iq:version'><name>e2e test</name><version>1.0</version><os>Fedora</os></query></iq>"), partial(expect_stanza, "/iq[@from='#foo%{irc_server_one}/{nick_one}'][@type='result'][@to='{jid_one}/{resource_one}'][@id='first_version']/version:query/version:name[text()='e2e test (through the biboumi gateway) 1.0 Fedora']"), @@ -966,12 +966,12 @@ if __name__ == '__main__': "<iq type='get' from='{jid_one}/{resource_two}' id='second_version' to='#foo%{irc_server_one}/{nick_one}'><query xmlns='jabber:iq:version' /></iq>"), # We receive our own request. Note that we don't know the to value, it could be one of our two resources. partial(expect_stanza, - "/iq[@from='{lower_nick_one}!{irc_server_one}'][@type='get'][@to]", + "/iq[@from='{lower_nick_one}%{irc_server_one}'][@type='get'][@to]", after = (partial(save_value, "to", partial(extract_attribute, "/iq", "to")), partial(save_value, "id", partial(extract_attribute, "/iq", "id")))), # Respond to the request, using the extracted 'to' value as our 'from' partial(send_stanza, - "<iq type='result' to='{lower_nick_one}!{irc_server_one}' id='{id}' from='{to}'><query xmlns='jabber:iq:version'><name>e2e test</name><version>1.0</version><os>Fedora</os></query></iq>"), + "<iq type='result' to='{lower_nick_one}%{irc_server_one}' id='{id}' from='{to}'><query xmlns='jabber:iq:version'><name>e2e test</name><version>1.0</version><os>Fedora</os></query></iq>"), partial(expect_stanza, "/iq[@from='#foo%{irc_server_one}/{nick_one}'][@type='result'][@to='{jid_one}/{resource_two}'][@id='second_version']"), @@ -980,12 +980,12 @@ if __name__ == '__main__': "<iq type='get' from='{jid_one}/{resource_one}' id='second_version' to='#foo%{irc_server_one}/{nick_one}'><query xmlns='jabber:iq:version' /></iq>"), # We receive our own request. Note that we don't know the to value, it could be one of our two resources. partial(expect_stanza, - "/iq[@from='{lower_nick_one}!{irc_server_one}'][@type='get'][@to]", + "/iq[@from='{lower_nick_one}%{irc_server_one}'][@type='get'][@to]", after = (partial(save_value, "to", partial(extract_attribute, "/iq", "to")), partial(save_value, "id", partial(extract_attribute, "/iq", "id")))), # Respond to the request, using the extracted 'to' value as our 'from' partial(send_stanza, - "<iq type='result' to='{lower_nick_one}!{irc_server_one}' id='{id}' from='{to}'><query xmlns='jabber:iq:version'><name>e2e test</name><version>1.0</version><os>Fedora</os></query></iq>"), + "<iq type='result' to='{lower_nick_one}%{irc_server_one}' id='{id}' from='{to}'><query xmlns='jabber:iq:version'><name>e2e test</name><version>1.0</version><os>Fedora</os></query></iq>"), partial(expect_stanza, "/iq[@from='#foo%{irc_server_one}/{nick_one}'][@type='result'][@to='{jid_one}/{resource_one}'][@id='second_version']"), ]), diff --git a/tests/iid.cpp b/tests/iid.cpp index 74d010d..b42b9e5 100644 --- a/tests/iid.cpp +++ b/tests/iid.cpp @@ -47,66 +47,60 @@ namespace Catch TEST_CASE("Iid creation") { - Iid iid1("foo!irc.example.org"); - CHECK(std::to_string(iid1) == "foo!irc.example.org"); + const std::set<char> chantypes {'#', '&'}; + Iid iid1("foo%irc.example.org", chantypes); + CHECK(std::to_string(iid1) == "foo%irc.example.org"); CHECK(iid1.get_local() == "foo"); CHECK(iid1.get_server() == "irc.example.org"); - CHECK(!iid1.is_channel); - CHECK(iid1.is_user); + CHECK(iid1.type == Iid::Type::User); - Iid iid2("#test%irc.example.org"); + Iid iid2("#test%irc.example.org", chantypes); CHECK(std::to_string(iid2) == "#test%irc.example.org"); CHECK(iid2.get_local() == "#test"); CHECK(iid2.get_server() == "irc.example.org"); - CHECK(iid2.is_channel); - CHECK(!iid2.is_user); + CHECK(iid2.type == Iid::Type::Channel); - Iid iid3("%irc.example.org"); + Iid iid3("%irc.example.org", chantypes); CHECK(std::to_string(iid3) == "%irc.example.org"); - CHECK(iid3.get_local() == ""); + CHECK(iid3.get_local().empty()); CHECK(iid3.get_server() == "irc.example.org"); - CHECK(iid3.is_channel); - CHECK(!iid3.is_user); + CHECK(iid3.type == Iid::Type::Channel); - Iid iid4("irc.example.org"); + Iid iid4("irc.example.org", chantypes); CHECK(std::to_string(iid4) == "irc.example.org"); CHECK(iid4.get_local() == ""); CHECK(iid4.get_server() == "irc.example.org"); - CHECK(!iid4.is_channel); - CHECK(!iid4.is_user); + CHECK(iid4.type == Iid::Type::Server); - Iid iid5("nick!"); - CHECK(std::to_string(iid5) == "nick!"); + Iid iid5("nick%", chantypes); + CHECK(std::to_string(iid5) == "nick%"); CHECK(iid5.get_local() == "nick"); CHECK(iid5.get_server() == ""); - CHECK(!iid5.is_channel); - CHECK(iid5.is_user); + CHECK(iid5.type == Iid::Type::User); - Iid iid6("##channel%"); + Iid iid6("##channel%", chantypes); CHECK(std::to_string(iid6) == "##channel%"); CHECK(iid6.get_local() == "##channel"); CHECK(iid6.get_server() == ""); - CHECK(iid6.is_channel); - CHECK(!iid6.is_user); + CHECK(iid6.type == Iid::Type::Channel); } TEST_CASE("Iid creation in fixed_server mode") { Config::set("fixed_irc_server", "fixed.example.com", false); - Iid iid1("foo!irc.example.org"); - CHECK(std::to_string(iid1) == "foo!"); - CHECK(iid1.get_local() == "foo"); + const std::set<char> chantypes {'#', '&'}; + Iid iid1("foo%irc.example.org", chantypes); + CHECK(std::to_string(iid1) == "foo%irc.example.org"); + CHECK(iid1.get_local() == "foo%irc.example.org"); CHECK(iid1.get_server() == "fixed.example.com"); - CHECK(!iid1.is_channel); - CHECK(iid1.is_user); + CHECK(iid1.type == Iid::Type::User); - Iid iid2("#test%irc.example.org"); + Iid iid2("#test%irc.example.org", chantypes); CHECK(std::to_string(iid2) == "#test%irc.example.org"); CHECK(iid2.get_local() == "#test%irc.example.org"); CHECK(iid2.get_server() == "fixed.example.com"); - CHECK(iid2.is_channel); - CHECK(!iid2.is_user); + CHECK(iid2.type == Iid::Type::Channel); // Note that it is impossible to adress the IRC server directly, or to // use the virtual channel, in that mode @@ -114,17 +108,15 @@ TEST_CASE("Iid creation in fixed_server mode") // Iid iid3("%irc.example.org"); // Iid iid4("irc.example.org"); - Iid iid5("nick!"); - CHECK(std::to_string(iid5) == "nick!"); + Iid iid5("nick", chantypes); + CHECK(std::to_string(iid5) == "nick"); CHECK(iid5.get_local() == "nick"); CHECK(iid5.get_server() == "fixed.example.com"); - CHECK(!iid5.is_channel); - CHECK(iid5.is_user); + CHECK(iid5.type == Iid::Type::User); - Iid iid6("##channel%"); + Iid iid6("##channel%", chantypes); CHECK(std::to_string(iid6) == "##channel%"); CHECK(iid6.get_local() == "##channel%"); CHECK(iid6.get_server() == "fixed.example.com"); - CHECK(iid6.is_channel); - CHECK(!iid6.is_user); + CHECK(iid6.type == Iid::Type::Channel); } |