summaryrefslogtreecommitdiff
path: root/src/bridge/bridge.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/bridge/bridge.cpp')
-rw-r--r--src/bridge/bridge.cpp112
1 files changed, 71 insertions, 41 deletions
diff --git a/src/bridge/bridge.cpp b/src/bridge/bridge.cpp
index 7a0157a..71c0ea4 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();
}
}
@@ -223,6 +224,27 @@ 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](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()])
+ this->xmpp.send_muc_message(std::to_string(iid), irc->get_own_nick(), this->make_xmpp_body(line),
+ this->user_jid + "/" + resource, uuid, id);
+ };
+
if (line.substr(0, 5) == "/mode")
{
std::vector<std::string> args = utils::split(line.substr(5), ' ', false);
@@ -231,22 +253,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 +460,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 +747,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 +843,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,9 +851,11 @@ 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()])
{
@@ -891,9 +920,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 +994,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 +1038,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 +1181,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 +1253,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);