summaryrefslogtreecommitdiff
path: root/src/xmpp/biboumi_component.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/xmpp/biboumi_component.cpp')
-rw-r--r--src/xmpp/biboumi_component.cpp139
1 files changed, 103 insertions, 36 deletions
diff --git a/src/xmpp/biboumi_component.cpp b/src/xmpp/biboumi_component.cpp
index be34873..f49b3b6 100644
--- a/src/xmpp/biboumi_component.cpp
+++ b/src/xmpp/biboumi_component.cpp
@@ -102,8 +102,8 @@ void BiboumiComponent::shutdown()
void BiboumiComponent::clean()
{
- auto it = this->bridges.begin();
- while (it != this->bridges.end())
+ auto it = std::begin(this->bridges);
+ while (it != std::end(this->bridges))
{
it->second->clean();
if (it->second->active_clients() == 0)
@@ -158,35 +158,51 @@ void BiboumiComponent::handle_presence(const Stanza& stanza)
{
const std::string own_nick = bridge->get_own_nick(iid);
const XmlNode* x = stanza.get_child("x", MUC_NS);
- const XmlNode* password = x ? x->get_child("password", MUC_NS): nullptr;
- const XmlNode* history = x ? x->get_child("history", MUC_NS): nullptr;
- HistoryLimit history_limit;
- if (history)
+ const IrcClient* irc = bridge->find_irc_client(iid.get_server());
+ // if there is no <x/>, this is a presence status update, we don’t care about those
+ if (x)
{
- const auto seconds = history->get_tag("seconds");
- if (!seconds.empty())
+ const XmlNode* password = x->get_child("password", MUC_NS);
+ const XmlNode* history = x->get_child("history", MUC_NS);
+ HistoryLimit history_limit;
+ if (history)
{
- const auto now = std::chrono::system_clock::now();
- std::time_t timestamp = std::chrono::system_clock::to_time_t(now);
- int int_seconds = std::atoi(seconds.data());
- timestamp -= int_seconds;
- history_limit.since = utils::to_string(timestamp);
+ const auto seconds = history->get_tag("seconds");
+ if (!seconds.empty())
+ {
+ const auto now = std::chrono::system_clock::now();
+ std::time_t timestamp = std::chrono::system_clock::to_time_t(now);
+ int int_seconds = std::atoi(seconds.data());
+ timestamp -= int_seconds;
+ history_limit.since = utils::to_string(timestamp);
+ }
+ const auto since = history->get_tag("since");
+ if (!since.empty())
+ history_limit.since = since;
+ const auto maxstanzas = history->get_tag("maxstanzas");
+ if (!maxstanzas.empty())
+ history_limit.stanzas = std::atoi(maxstanzas.data());
+ // Ignore any other value, because this is too complex to implement,
+ // so I won’t do it.
+ if (history->get_tag("maxchars") == "0")
+ history_limit.stanzas = 0;
+ }
+ bridge->join_irc_channel(iid, to.resource, password ? password->get_inner(): "",
+ from.resource, history_limit);
+ }
+ else
+ {
+ if (irc)
+ {
+ const auto chan = irc->find_channel(iid.get_local());
+ if (chan && chan->joined)
+ bridge->send_irc_nick_change(iid, to.resource, from.resource);
+ else
+ { // send an error if we are not joined yet, instead of treating it as a join
+ this->send_stanza_error("presence", from_str, to_str, id, "modify", "not-acceptable", "You are not joined to this MUC.");
+ }
}
- const auto since = history->get_tag("since");
- if (!since.empty())
- history_limit.since = since;
- const auto maxstanzas = history->get_tag("maxstanzas");
- if (!maxstanzas.empty())
- history_limit.stanzas = std::atoi(maxstanzas.data());
- // Ignore any other value, because this is too complex to implement,
- // so I won’t do it.
- if (history->get_tag("maxchars") == "0")
- history_limit.stanzas = 0;
}
- bridge->join_irc_channel(iid, to.resource, password ? password->get_inner(): "",
- from.resource, history_limit, x != nullptr);
- if (!own_nick.empty() && own_nick != to.resource)
- bridge->send_irc_nick_change(iid, to.resource, from.resource);
}
else if (type == "unavailable")
{
@@ -273,9 +289,10 @@ void BiboumiComponent::handle_message(const Stanza& stanza)
std::string error_type("cancel");
std::string error_name("internal-server-error");
- utils::ScopeGuard stanza_error([this, &from_str, &to_str, &id, &error_type, &error_name](){
+ std::string error_text{};
+ utils::ScopeGuard stanza_error([this, &from_str, &to_str, &id, &error_type, &error_name, &error_text](){
this->send_stanza_error("message", from_str, to_str, id,
- error_type, error_name, "");
+ error_type, error_name, error_text);
});
const XmlNode* body = stanza.get_child("body", COMPONENT_NS);
@@ -284,7 +301,34 @@ void BiboumiComponent::handle_message(const Stanza& stanza)
{
if (body && !body->get_inner().empty())
{
- bridge->send_channel_message(iid, body->get_inner(), id);
+ if (bridge->is_resource_in_chan(iid.to_tuple(), from.resource))
+ {
+ // Extract some XML nodes that we must include in the
+ // reflection (if any), because XMPP says so
+ std::vector<XmlNode> nodes_to_reflect;
+ const XmlNode* origin_id = stanza.get_child("origin-id", STABLE_ID_NS);
+ if (origin_id)
+ nodes_to_reflect.push_back(*origin_id);
+ const auto own_address = std::to_string(iid) + '@' + this->served_hostname;
+ for (const XmlNode* stanza_id: stanza.get_children("stanza-id", STABLE_ID_NS))
+ {
+ // Stanza ID generating entities, which encounter a
+ // <stanza-id/> element where the 'by' attribute matches
+ // the 'by' attribute they would otherwise set, MUST
+ // delete that element even if they are not adding their
+ // own stanza ID.
+ if (stanza_id->get_tag("by") != own_address)
+ nodes_to_reflect.push_back(*stanza_id);
+ }
+ bridge->send_channel_message(iid, body->get_inner(), id, std::move(nodes_to_reflect));
+ }
+ else
+ {
+ error_type = "modify";
+ error_name = "not-acceptable";
+ error_text = "You are not a participant in this room.";
+ return;
+ }
}
const XmlNode* subject = stanza.get_child("subject", COMPONENT_NS);
if (subject)
@@ -350,7 +394,6 @@ void BiboumiComponent::handle_message(const Stanza& stanza)
this->send_invitation_from_fulljid(std::to_string(iid), invite_to, from_str);
}
}
-
}
} catch (const IRCNotConnected& ex)
{
@@ -514,7 +557,11 @@ void BiboumiComponent::handle_iq(const Stanza& stanza)
{
if (node.empty())
{
- this->send_irc_channel_disco_info(id, from, to_str);
+ const IrcClient* irc_client = bridge->find_irc_client(iid.get_server());
+ const IrcChannel* irc_channel{};
+ if (irc_client)
+ irc_channel = irc_client->find_channel(iid.get_local());
+ this->send_irc_channel_disco_info(id, from, to_str, irc_channel);
stanza_error.disable();
}
else if (node == MUC_TRAFFIC_NS)
@@ -592,7 +639,6 @@ void BiboumiComponent::handle_iq(const Stanza& stanza)
const XmlNode* max = set_node->get_child("max", RSM_NS);
if (max)
rs_info.max = std::atoi(max->get_inner().data());
-
}
if (rs_info.max == -1)
rs_info.max = 100;
@@ -751,7 +797,7 @@ bool BiboumiComponent::handle_mam_request(const Stanza& stanza)
if (limit < 0 || limit > 100)
limit = 100;
auto result = Database::get_muc_logs(from.bare(), iid.get_local(), iid.get_server(),
- limit,
+ static_cast<std::size_t>(limit),
start, end,
reference_record_id, paging_order);
bool complete = std::get<bool>(result);
@@ -964,7 +1010,8 @@ void BiboumiComponent::send_irc_channel_muc_traffic_info(const std::string& id,
this->send_stanza(iq);
}
-void BiboumiComponent::send_irc_channel_disco_info(const std::string& id, const std::string& jid_to, const std::string& jid_from)
+void BiboumiComponent::send_irc_channel_disco_info(const std::string& id, const std::string& jid_to,
+ const std::string& jid_from, const IrcChannel* irc_channel)
{
Jid from(jid_from);
Iid iid(from.local, {});
@@ -980,11 +1027,31 @@ void BiboumiComponent::send_irc_channel_disco_info(const std::string& id, const
identity["category"] = "conference";
identity["type"] = "irc";
identity["name"] = ""s + iid.get_local() + " on " + iid.get_server();
- for (const char *ns: {DISCO_INFO_NS, MUC_NS, ADHOC_NS, PING_NS, MAM_NS, VERSION_NS, STABLE_MUC_ID_NS})
+ for (const char *ns: {DISCO_INFO_NS, MUC_NS, ADHOC_NS, PING_NS, MAM_NS, VERSION_NS, STABLE_MUC_ID_NS, SELF_PING_FLAG, "muc_nonanonymous", STABLE_ID_NS})
{
XmlSubNode feature(query, "feature");
feature["var"] = ns;
}
+
+ XmlSubNode x(query, "x");
+ x["xmlns"] = DATAFORM_NS;
+ x["type"] = "result";
+ {
+ XmlSubNode field(x, "field");
+ field["var"] = "FORM_TYPE";
+ field["type"] = "hidden";
+ XmlSubNode value(field, "value");
+ value.set_inner("http://jabber.org/protocol/muc#roominfo");
+ }
+
+ if (irc_channel && irc_channel->joined)
+ {
+ XmlSubNode field(x, "field");
+ field["var"] = "muc#roominfo_occupants";
+ field["label"] = "Number of occupants";
+ XmlSubNode value(field, "value");
+ value.set_inner(std::to_string(irc_channel->get_users().size()));
+ }
}
this->send_stanza(iq);
}