summaryrefslogtreecommitdiff
path: root/src/bridge
diff options
context:
space:
mode:
Diffstat (limited to 'src/bridge')
-rw-r--r--src/bridge/bridge.cpp135
-rw-r--r--src/bridge/bridge.hpp21
-rw-r--r--src/bridge/history_limit.hpp2
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
{