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.cpp89
1 files changed, 61 insertions, 28 deletions
diff --git a/src/xmpp/biboumi_component.cpp b/src/xmpp/biboumi_component.cpp
index 8775869..6dc5fc5 100644
--- a/src/xmpp/biboumi_component.cpp
+++ b/src/xmpp/biboumi_component.cpp
@@ -7,6 +7,7 @@
#include <xmpp/adhoc_command.hpp>
#include <xmpp/biboumi_adhoc_commands.hpp>
#include <bridge/list_element.hpp>
+#include <utils/encoding.hpp>
#include <config/config.hpp>
#include <utils/time.hpp>
#include <xmpp/jid.hpp>
@@ -147,13 +148,10 @@ void BiboumiComponent::handle_presence(const Stanza& stanza)
try {
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
+ { // presence toward a MUC that corresponds to an irc channel
if (type.empty())
{
const std::string own_nick = bridge->get_own_nick(iid);
- if (!own_nick.empty() && own_nick != to.resource)
- bridge->send_irc_nick_change(iid, to.resource, from.resource);
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;
@@ -181,7 +179,9 @@ void BiboumiComponent::handle_presence(const Stanza& stanza)
history_limit.stanzas = 0;
}
bridge->join_irc_channel(iid, to.resource, password ? password->get_inner(): "",
- from.resource, history_limit);
+ 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")
{
@@ -279,7 +279,7 @@ void BiboumiComponent::handle_message(const Stanza& stanza)
{
if (body && !body->get_inner().empty())
{
- bridge->send_channel_message(iid, body->get_inner());
+ bridge->send_channel_message(iid, body->get_inner(), id);
}
const XmlNode* subject = stanza.get_child("subject", COMPONENT_NS);
if (subject)
@@ -466,8 +466,13 @@ void BiboumiComponent::handle_iq(const Stanza& stanza)
#ifdef USE_DATABASE
else if ((query = stanza.get_child("query", MAM_NS)))
{
- if (this->handle_mam_request(stanza))
- stanza_error.disable();
+ try {
+ if (this->handle_mam_request(stanza))
+ stanza_error.disable();
+ } catch (const Database::RecordNotFound& exc) {
+ error_name = "item-not-found";
+ return;
+ }
}
else if ((query = stanza.get_child("query", MUC_OWNER_NS)))
{
@@ -546,24 +551,21 @@ void BiboumiComponent::handle_iq(const Stanza& stanza)
if (to.local.empty())
{ // Get biboumi's adhoc commands
this->send_adhoc_commands_list(id, from, this->served_hostname,
- (Config::get("admin", "") ==
- from_jid.bare()),
+ Config::is_in_list("admin", from_jid.bare()),
this->adhoc_commands_handler);
stanza_error.disable();
}
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", "") ==
- from_jid.bare()),
+ Config::is_in_list("admin", from_jid.bare()),
this->irc_server_adhoc_commands_handler);
stanza_error.disable();
}
else if (iid.type == Iid::Type::Channel && to.resource.empty())
{ // Get the channel's adhoc commands
this->send_adhoc_commands_list(id, from, to_str,
- (Config::get("admin", "") ==
- from_jid.bare()),
+ Config::is_in_list("admin", from_jid.bare()),
this->irc_channel_adhoc_commands_handler);
stanza_error.disable();
}
@@ -714,29 +716,58 @@ bool BiboumiComponent::handle_mam_request(const Stanza& stanza)
}
const XmlNode* set = query->get_child("set", RSM_NS);
int limit = -1;
+ Id::real_type reference_record_id{Id::unset_value};
+ Database::Paging paging_order{Database::Paging::first};
if (set)
{
const XmlNode* max = set->get_child("max", RSM_NS);
if (max)
limit = std::atoi(max->get_inner().data());
+ const XmlNode* after = set->get_child("after", RSM_NS);
+ if (after)
+ {
+ auto after_record = Database::get_muc_log(from.bare(), iid.get_local(), iid.get_server(),
+ after->get_inner(), start, end);
+ reference_record_id = after_record.col<Id>();
+ }
+ const XmlNode* before = set->get_child("before", RSM_NS);
+ if (before)
+ {
+ paging_order = Database::Paging::last;
+ if (!before->get_inner().empty())
+ {
+ auto before_record = Database::get_muc_log(from.bare(), iid.get_local(), iid.get_server(), before->get_inner(), start, end);
+ reference_record_id = before_record.col<Id>();
+ }
+ }
}
- // If the archive is really big, and the client didn’t specify any
- // limit, we avoid flooding it: we set an arbitrary max limit.
- if (limit == -1 && start.empty() && end.empty())
+ // Do not send more than 100 messages, even if the client asked for more,
+ // or if it didn’t specify any limit.
+ // 101 is just a trick to know if there are more available messages.
+ // If our query returns 101 message, we know it’s incomplete, but we
+ // still send only 100
+ if ((limit == -1 && start.empty() && end.empty())
+ || limit > 100)
+ limit = 101;
+ auto lines = Database::get_muc_logs(from.bare(), iid.get_local(), iid.get_server(), limit, start, end, reference_record_id, paging_order);
+ bool complete = true;
+ if (lines.size() > 100)
{
- limit = 100;
+ complete = false;
+ lines.erase(lines.begin(), std::prev(lines.end(), 100));
}
- const auto lines = Database::get_muc_logs(from.bare(), iid.get_local(), iid.get_server(), limit, start, end);
for (const Database::MucLogLine& line: lines)
- {
- if (!line.col<Database::Nick>().empty())
- this->send_archived_message(line, to.full(), from.full(), query_id);
- }
+ {
+ if (!line.col<Database::Nick>().empty())
+ this->send_archived_message(line, to.full(), from.full(), query_id);
+ }
{
auto fin_ptr = std::make_unique<XmlNode>("fin");
{
XmlNode& fin = *(fin_ptr.get());
fin["xmlns"] = MAM_NS;
+ if (complete)
+ fin["complete"] = "true";
XmlSubNode set(fin, "set");
set["xmlns"] = RSM_NS;
if (!lines.empty())
@@ -881,7 +912,7 @@ void BiboumiComponent::send_self_disco_info(const std::string& id, const std::st
identity["category"] = "conference";
identity["type"] = "irc";
identity["name"] = "Biboumi XMPP-IRC gateway";
- for (const char *ns: {DISCO_INFO_NS, MUC_NS, ADHOC_NS, PING_NS, MAM_NS, VERSION_NS})
+ for (const char *ns: {DISCO_INFO_NS, MUC_NS, ADHOC_NS, PING_NS, MAM_NS, VERSION_NS, STABLE_MUC_ID_NS})
{
XmlSubNode feature(query, "feature");
feature["var"] = ns;
@@ -905,7 +936,7 @@ void BiboumiComponent::send_irc_server_disco_info(const std::string& id, const s
identity["category"] = "conference";
identity["type"] = "irc";
identity["name"] = "IRC server " + from.local + " over Biboumi";
- for (const char *ns: {DISCO_INFO_NS, MUC_NS, ADHOC_NS, PING_NS, MAM_NS, VERSION_NS})
+ for (const char *ns: {DISCO_INFO_NS, MUC_NS, ADHOC_NS, PING_NS, MAM_NS, VERSION_NS, STABLE_MUC_ID_NS})
{
XmlSubNode feature(query, "feature");
feature["var"] = ns;
@@ -947,8 +978,8 @@ void BiboumiComponent::send_irc_channel_disco_info(const std::string& id, const
XmlSubNode identity(query, "identity");
identity["category"] = "conference";
identity["type"] = "irc";
- identity["name"] = "IRC channel " + iid.get_local() + " from server " + iid.get_server() + " over biboumi";
- for (const char *ns: {DISCO_INFO_NS, MUC_NS, ADHOC_NS, PING_NS, MAM_NS, VERSION_NS})
+ 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})
{
XmlSubNode feature(query, "feature");
feature["var"] = ns;
@@ -1010,7 +1041,9 @@ void BiboumiComponent::send_iq_room_list_result(const std::string& id, const std
for (auto it = begin; it != end; ++it)
{
XmlSubNode item(query, "item");
- item["jid"] = it->channel + "@" + this->served_hostname;
+ std::string channel_name = it->channel;
+ xep0106::encode(channel_name);
+ item["jid"] = channel_name + "@" + this->served_hostname;
}
if ((rs_info.max >= 0 || !rs_info.after.empty() || !rs_info.before.empty()))