diff options
Diffstat (limited to 'src/bridge')
-rw-r--r-- | src/bridge/bridge.cpp | 135 | ||||
-rw-r--r-- | src/bridge/bridge.hpp | 21 | ||||
-rw-r--r-- | src/bridge/history_limit.hpp | 2 |
3 files changed, 98 insertions, 60 deletions
diff --git a/src/bridge/bridge.cpp b/src/bridge/bridge.cpp index 7a0157a..424c72a 100644 --- a/src/bridge/bridge.cpp +++ b/src/bridge/bridge.cpp @@ -63,7 +63,8 @@ void Bridge::shutdown(const std::string& exit_message) { for (auto& pair: this->irc_clients) { - pair.second->send_quit_command(exit_message); + std::unique_ptr<IrcClient>& irc = pair.second; + irc->send_quit_command(exit_message); } } @@ -133,11 +134,11 @@ IrcClient* Bridge::make_irc_client(const std::string& hostname, const std::strin realname = this->get_bare_jid(); } this->irc_clients.emplace(hostname, - std::make_shared<IrcClient>(this->poller, hostname, + std::make_unique<IrcClient>(this->poller, hostname, nickname, username, realname, jid.domain, *this)); - std::shared_ptr<IrcClient> irc = this->irc_clients.at(hostname); + std::unique_ptr<IrcClient>& irc = this->irc_clients.at(hostname); return irc.get(); } } @@ -169,8 +170,7 @@ IrcClient* Bridge::find_irc_client(const std::string& hostname) const bool Bridge::join_irc_channel(const Iid& iid, std::string nickname, const std::string& password, const std::string& resource, - HistoryLimit history_limit, - const bool force_join) + HistoryLimit history_limit) { const auto& hostname = iid.get_server(); #ifdef USE_DATABASE @@ -184,18 +184,18 @@ bool Bridge::join_irc_channel(const Iid& iid, std::string nickname, auto res_in_chan = this->is_resource_in_chan(ChannelKey{iid.get_local(), hostname}, resource); if (!res_in_chan) this->add_resource_to_chan(ChannelKey{iid.get_local(), hostname}, resource); - if (irc->is_channel_joined(iid.get_local()) == false) + if (!irc->is_channel_joined(iid.get_local())) { irc->send_join_command(iid.get_local(), password); return true; - } else if (!res_in_chan || force_join) { - // See https://github.com/xsf/xeps/pull/499 for the force_join argument + } else { + // See https://github.com/xsf/xeps/pull/499 this->generate_channel_join_for_resource(iid, resource); } return false; } -void Bridge::send_channel_message(const Iid& iid, const std::string& body, std::string id) +void Bridge::send_channel_message(const Iid& iid, const std::string& body, std::string id, std::vector<XmlNode> nodes_to_reflect) { if (iid.get_server().empty()) { @@ -223,6 +223,33 @@ void Bridge::send_channel_message(const Iid& iid, const std::string& body, std:: bool first = true; for (const std::string& line: lines) { + std::string uuid; +#ifdef USE_DATABASE + const auto xmpp_body = this->make_xmpp_body(line); + if (this->record_history) + uuid = Database::store_muc_message(this->get_bare_jid(), iid.get_local(), iid.get_server(), std::chrono::system_clock::now(), + std::get<0>(xmpp_body), irc->get_own_nick()); +#endif + if (!first || id.empty()) + id = utils::gen_uuid(); + + MessageCallback mirror_to_all_resources = [this, iid, uuid, id, nodes_to_reflect](const IrcClient* irc, const IrcMessage& message) { + std::string line = message.arguments[1]; + // “temporary” workaround for \01ACTION…\01 -> /me messages + if ((line.size() > strlen("\01ACTION\01")) && + (line.substr(0, 7) == "\01ACTION") && line[line.size() - 1] == '\01') + line = "/me " + line.substr(8, line.size() - 9); + for (const auto& resource: this->resources_in_chan[iid.to_tuple()]) + { + auto stanza = this->xmpp.make_muc_message(std::to_string(iid), irc->get_own_nick(), this->make_xmpp_body(line), + this->user_jid + "/" + + resource, uuid, id); + for (const auto& node: nodes_to_reflect) + stanza.add_child(node); + this->xmpp.send_stanza(stanza); + } + }; + if (line.substr(0, 5) == "/mode") { std::vector<std::string> args = utils::split(line.substr(5), ' ', false); @@ -231,22 +258,11 @@ void Bridge::send_channel_message(const Iid& iid, const std::string& body, std:: // XMPP user, that’s not a textual message. } else if (line.substr(0, 4) == "/me ") - irc->send_channel_message(iid.get_local(), action_prefix + line.substr(4) + "\01"); + irc->send_channel_message(iid.get_local(), action_prefix + line.substr(4) + "\01", + std::move(mirror_to_all_resources)); else - irc->send_channel_message(iid.get_local(), line); + irc->send_channel_message(iid.get_local(), line, std::move(mirror_to_all_resources)); - std::string uuid; -#ifdef USE_DATABASE - const auto xmpp_body = this->make_xmpp_body(line); - if (this->record_history) - uuid = Database::store_muc_message(this->get_bare_jid(), iid.get_local(), iid.get_server(), std::chrono::system_clock::now(), - std::get<0>(xmpp_body), irc->get_own_nick()); -#endif - if (!first || id.empty()) - id = utils::gen_uuid(); - for (const auto& resource: this->resources_in_chan[iid.to_tuple()]) - this->xmpp.send_muc_message(std::to_string(iid), irc->get_own_nick(), this->make_xmpp_body(line), - this->user_jid + "/" + resource, uuid, id); first = false; } } @@ -449,9 +465,8 @@ void Bridge::leave_irc_channel(Iid&& iid, const std::string& status_message, con true, true, resource, irc); this->remove_resource_from_chan(key, resource); } - if (this->number_of_channels_the_resource_is_in(iid.get_server(), resource) == 0) - this->remove_resource_from_server(iid.get_server(), resource); - + if (this->number_of_channels_the_resource_is_in(iid.get_server(), resource) == 0) + this->remove_resource_from_server(iid.get_server(), resource); } void Bridge::send_irc_nick_change(const Iid& iid, const std::string& new_nick, const std::string& requesting_resource) @@ -737,10 +752,27 @@ void Bridge::send_irc_participant_ping_request(const Iid& iid, const std::string IrcChannel* chan = irc->get_channel(iid.get_local()); if (!chan->joined || !this->is_resource_in_chan(iid.to_tuple(), from.resource)) { - this->xmpp.send_stanza_error("iq", to_jid, from_jid, iq_id, "cancel", "not-allowed", + this->xmpp.send_stanza_error("iq", to_jid, from_jid, iq_id, "cancel", "not-acceptable", "", true); return; } + if (chan->get_self()->nick == nick) + { + // XEP-0410 self-ping optimisation: always reply without going the full + // round-trip through IRC and possibly another XMPP client. See the XEP + // for details. + Jid iq_from(from_jid); + iq_from.local = std::to_string(iid); + iq_from.resource = nick; + + Stanza iq("iq"); + iq["from"] = iq_from.full(); + iq["to"] = to_jid; + iq["id"] = iq_id; + iq["type"] = "result"; + this->xmpp.send_stanza(iq); + return; + } if (chan->get_self()->nick != nick && !chan->find_user(nick)) { this->xmpp.send_stanza_error("iq", to_jid, from_jid, iq_id, "cancel", "item-not-found", @@ -816,7 +848,7 @@ void Bridge::send_irc_version_request(const std::string& irc_hostname, const std this->add_waiting_irc(std::move(cb)); } -void Bridge::send_message(const Iid& iid, const std::string& nick, const std::string& body, const bool muc) +void Bridge::send_message(const Iid& iid, const std::string& nick, const std::string& body, const bool muc, const bool log) { const auto encoding = in_encoding_for(*this, iid); std::string uuid{}; @@ -824,14 +856,18 @@ void Bridge::send_message(const Iid& iid, const std::string& nick, const std::st { #ifdef USE_DATABASE const auto xmpp_body = this->make_xmpp_body(body, encoding); - if (!nick.empty() && this->record_history) + if (log && this->record_history) uuid = Database::store_muc_message(this->get_bare_jid(), iid.get_local(), iid.get_server(), std::chrono::system_clock::now(), std::get<0>(xmpp_body), nick); +#else + (void)log; #endif for (const auto& resource: this->resources_in_chan[iid.to_tuple()]) { - this->xmpp.send_muc_message(std::to_string(iid), nick, this->make_xmpp_body(body, encoding), - this->user_jid + "/" + resource, uuid, utils::gen_uuid()); + auto stanza = this->xmpp.make_muc_message(std::to_string(iid), nick, this->make_xmpp_body(body, encoding), + this->user_jid + "/" + + resource, uuid, utils::gen_uuid()); + this->xmpp.send_stanza(stanza); } } else @@ -891,9 +927,7 @@ void Bridge::send_muc_leave(const Iid& iid, const IrcUser& user, for (const auto& r: resources_in_chan) if (this->number_of_channels_the_resource_is_in(iid.get_server(), r) == 0) this->remove_resource_from_server(iid.get_server(), r); - } - } IrcClient* irc = this->find_irc_client(iid.get_server()); if (self && irc && irc->number_of_joined_channels() == 0) @@ -967,8 +1001,18 @@ void Bridge::send_user_join(const std::string& hostname, const std::string& chan std::string encoded_chan_name(chan_name); xep0106::encode(encoded_chan_name); - this->xmpp.send_user_join(encoded_chan_name + utils::empty_if_fixed_server("%" + hostname), user->nick, user->host, - affiliation, role, this->user_jid + "/" + resource, self); + std::string encoded_nick_name(user->nick); + xep0106::encode(encoded_nick_name); + + std::string full_jid = + encoded_nick_name + utils::empty_if_fixed_server("%" + hostname) + + "@" + this->xmpp.get_served_hostname(); + if (!user->host.empty()) + full_jid += "/" + user->host; + + this->xmpp.send_user_join(encoded_chan_name + utils::empty_if_fixed_server("%" + hostname), + user->nick, full_jid, affiliation, role, + this->user_jid + "/" + resource, self); } void Bridge::send_topic(const std::string& hostname, const std::string& chan_name, const std::string& topic, @@ -1001,11 +1045,13 @@ void Bridge::send_room_history(const std::string& hostname, const std::string& c void Bridge::send_room_history(const std::string& hostname, std::string chan_name, const std::string& resource, const HistoryLimit& history_limit) { #ifdef USE_DATABASE - const auto coptions = Database::get_irc_channel_options_with_server_and_global_default(this->user_jid, hostname, chan_name); - auto limit = coptions.col<Database::MaxHistoryLength>(); + const auto goptions = Database::get_global_options(this->user_jid); + auto limit = goptions.col<Database::MaxHistoryLength>(); + if (limit < 0) + limit = 20; if (history_limit.stanzas >= 0 && history_limit.stanzas < limit) limit = history_limit.stanzas; - const auto result = Database::get_muc_logs(this->user_jid, chan_name, hostname, limit, history_limit.since, {}, Id::unset_value, Database::Paging::last); + const auto result = Database::get_muc_logs(this->user_jid, chan_name, hostname, static_cast<std::size_t>(limit), history_limit.since, {}, Id::unset_value, Database::Paging::last); const auto& lines = std::get<1>(result); chan_name.append(utils::empty_if_fixed_server("%" + hostname)); for (const auto& line: lines) @@ -1142,12 +1188,12 @@ void Bridge::trigger_on_irc_message(const std::string& irc_hostname, const IrcMe } } -std::unordered_map<std::string, std::shared_ptr<IrcClient>>& Bridge::get_irc_clients() +std::unordered_map<std::string, std::unique_ptr<IrcClient>>& Bridge::get_irc_clients() { return this->irc_clients; } -const std::unordered_map<std::string, std::shared_ptr<IrcClient>>& Bridge::get_irc_clients() const +const std::unordered_map<std::string, std::unique_ptr<IrcClient>>& Bridge::get_irc_clients() const { return this->irc_clients; } @@ -1214,15 +1260,6 @@ void Bridge::remove_resource_from_server(const Bridge::IrcHostname& irc_hostname } } -bool Bridge::is_resource_in_server(const Bridge::IrcHostname& irc_hostname, const std::string& resource) const -{ - auto it = this->resources_in_server.find(irc_hostname); - if (it != this->resources_in_server.end()) - if (it->second.count(resource) == 1) - return true; - return false; -} - std::size_t Bridge::number_of_resources_in_chan(const Bridge::ChannelKey& channel) const { auto it = this->resources_in_chan.find(channel); diff --git a/src/bridge/bridge.hpp b/src/bridge/bridge.hpp index 8e7d9d7..6b15478 100644 --- a/src/bridge/bridge.hpp +++ b/src/bridge/bridge.hpp @@ -72,16 +72,14 @@ public: **/ /** - * Try to join an irc_channel, does nothing and return true if the channel - * was already joined. + * Try to join an irc_channel. */ bool join_irc_channel(const Iid& iid, std::string nickname, const std::string& password, const std::string& resource, - HistoryLimit history_limit, - const bool force_join); + HistoryLimit history_limit); - void send_channel_message(const Iid& iid, const std::string& body, std::string id); + void send_channel_message(const Iid& iid, const std::string& body, std::string id, std::vector<XmlNode> nodes_to_reflect); void send_private_message(const Iid& iid, const std::string& body, const std::string& type="PRIVMSG"); void send_raw_message(const std::string& hostname, const std::string& body); void leave_irc_channel(Iid&& iid, const std::string& status_message, const std::string& resource); @@ -164,9 +162,9 @@ public: void send_room_history(const std::string& hostname, const std::string& chan_name, const HistoryLimit& history_limit); void send_room_history(const std::string& hostname, std::string chan_name, const std::string& resource, const HistoryLimit& history_limit); /** - * Send a MUC message from some participant + * Send a message from a MUC participant or a direct message */ - void send_message(const Iid& iid, const std::string& nick, const std::string& body, const bool muc); + void send_message(const Iid& iid, const std::string& nick, const std::string& body, const bool muc, const bool log=true); /** * Send a presence of type error, from a room. */ @@ -241,8 +239,8 @@ public: * iq_responder_callback_t and remove the callback from the list. */ 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(); - const std::unordered_map<std::string, std::shared_ptr<IrcClient>>& get_irc_clients() const; + std::unordered_map<std::string, std::unique_ptr<IrcClient>>& get_irc_clients(); + const std::unordered_map<std::string, std::unique_ptr<IrcClient>>& get_irc_clients() const; std::set<char> get_chantypes(const std::string& hostname) const; #ifdef USE_DATABASE void set_record_history(const bool val); @@ -275,7 +273,7 @@ private: * One IrcClient for each IRC server we need to be connected to. * The pointer is shared by the bridge and the poller. */ - std::unordered_map<std::string, std::shared_ptr<IrcClient>> irc_clients; + std::unordered_map<std::string, std::unique_ptr<IrcClient>> irc_clients; /** * To communicate back with the XMPP component */ @@ -316,13 +314,14 @@ private: */ void add_resource_to_chan(const ChannelKey& channel, const std::string& resource); void remove_resource_from_chan(const ChannelKey& channel, const std::string& resource); +public: bool is_resource_in_chan(const ChannelKey& channel, const std::string& resource) const; +private: void remove_all_resources_from_chan(const ChannelKey& channel); std::size_t number_of_resources_in_chan(const ChannelKey& channel) const; void add_resource_to_server(const IrcHostname& irc_hostname, const std::string& resource); void remove_resource_from_server(const IrcHostname& irc_hostname, const std::string& resource); - bool is_resource_in_server(const IrcHostname& irc_hostname, const std::string& resource) const; size_t number_of_channels_the_resource_is_in(const std::string& irc_hostname, const std::string& resource) const; /** diff --git a/src/bridge/history_limit.hpp b/src/bridge/history_limit.hpp index 9c75256..93e36e1 100644 --- a/src/bridge/history_limit.hpp +++ b/src/bridge/history_limit.hpp @@ -1,5 +1,7 @@ #pragma once +#include <string> + // Default values means no limit struct HistoryLimit { |