summaryrefslogtreecommitdiff
path: root/src/xmpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/xmpp')
-rw-r--r--src/xmpp/adhoc_commands_handler.cpp2
-rw-r--r--src/xmpp/biboumi_adhoc_commands.cpp71
-rw-r--r--src/xmpp/biboumi_component.cpp89
-rw-r--r--src/xmpp/xmpp_component.cpp22
-rw-r--r--src/xmpp/xmpp_component.hpp12
5 files changed, 142 insertions, 54 deletions
diff --git a/src/xmpp/adhoc_commands_handler.cpp b/src/xmpp/adhoc_commands_handler.cpp
index bb48781..bc4c108 100644
--- a/src/xmpp/adhoc_commands_handler.cpp
+++ b/src/xmpp/adhoc_commands_handler.cpp
@@ -41,7 +41,7 @@ XmlNode AdhocCommandsHandler::handle_request(const std::string& executor_jid, co
XmlSubNode condition(error, STANZA_NS":item-not-found");
}
else if (command_it->second.is_admin_only() &&
- Config::get("admin", "") != jid.local + "@" + jid.domain)
+ !Config::is_in_list("admin", jid.bare()))
{
XmlSubNode error(command_node, ADHOC_NS":error");
error["type"] = "cancel";
diff --git a/src/xmpp/biboumi_adhoc_commands.cpp b/src/xmpp/biboumi_adhoc_commands.cpp
index bcdac39..53806d6 100644
--- a/src/xmpp/biboumi_adhoc_commands.cpp
+++ b/src/xmpp/biboumi_adhoc_commands.cpp
@@ -14,6 +14,7 @@
#ifdef USE_DATABASE
#include <database/database.hpp>
+#include <database/save.hpp>
#endif
#ifndef HAS_PUT_TIME
@@ -196,7 +197,7 @@ void ConfigureGlobalStep2(XmppComponent& xmpp_component, AdhocSession& session,
options.col<Database::GlobalPersistent>() = to_bool(value->get_inner());
}
- options.save(Database::db);
+ save(options, *Database::db);
command_node.delete_all_children();
XmlSubNode note(command_node, "note");
@@ -219,6 +220,7 @@ void ConfigureIrcServerStep1(XmppComponent&, AdhocSession& session, XmlNode& com
server_domain = target.local;
auto options = Database::get_irc_server_options(owner.local + "@" + owner.domain,
server_domain);
+ auto commands = Database::get_after_connection_commands(options);
XmlSubNode x(command_node, "jabber:x:data:x");
x["type"] = "form";
@@ -228,6 +230,19 @@ void ConfigureIrcServerStep1(XmppComponent&, AdhocSession& session, XmlNode& com
instructions.set_inner("Edit the form, to configure the settings of the IRC server " + server_domain);
{
+ XmlSubNode field(x, "field");
+ field["var"] = "address";
+ field["type"] = "text-single";
+ field["label"] = "Address";
+ field["desc"] = "The address (hostname or IP) to connect to.";
+ XmlSubNode value(field, "value");
+ if (options.col<Database::Address>().empty())
+ value.set_inner(server_domain);
+ else
+ value.set_inner(options.col<Database::Address>());
+ }
+
+ {
XmlSubNode ports(x, "field");
ports["var"] = "ports";
ports["type"] = "text-multi";
@@ -279,6 +294,20 @@ void ConfigureIrcServerStep1(XmppComponent&, AdhocSession& session, XmlNode& com
}
}
#endif
+
+ {
+ XmlSubNode field(x, "field");
+ field["var"] = "nick";
+ field["type"] = "text-single";
+ field["label"] = "Nickname";
+ field["desc"] = "If set, will override the nickname provided in the initial presence sent to join the first server channel";
+ if (!options.col<Database::Nick>().empty())
+ {
+ XmlSubNode value(field, "value");
+ value.set_inner(options.col<Database::Nick>());
+ }
+ }
+
{
XmlSubNode pass(x, "field");
pass["var"] = "pass";
@@ -294,14 +323,14 @@ void ConfigureIrcServerStep1(XmppComponent&, AdhocSession& session, XmlNode& com
{
XmlSubNode after_cnt_cmd(x, "field");
- after_cnt_cmd["var"] = "after_connect_command";
- after_cnt_cmd["type"] = "text-single";
- after_cnt_cmd["desc"] = "Custom IRC command sent after the connection is established with the server.";
- after_cnt_cmd["label"] = "After-connection IRC command";
- if (!options.col<Database::AfterConnectionCommand>().empty())
+ after_cnt_cmd["var"] = "after_connect_commands";
+ after_cnt_cmd["type"] = "text-multi";
+ after_cnt_cmd["desc"] = "Custom IRC commands sent after the connection is established with the server.";
+ after_cnt_cmd["label"] = "After-connection IRC commands";
+ for (const auto& command: commands)
{
XmlSubNode after_cnt_cmd_value(after_cnt_cmd, "value");
- after_cnt_cmd_value.set_inner(options.col<Database::AfterConnectionCommand>());
+ after_cnt_cmd_value.set_inner(command.col<Database::AfterConnectionCommand>());
}
}
@@ -371,10 +400,16 @@ void ConfigureIrcServerStep2(XmppComponent&, AdhocSession& session, XmlNode& com
server_domain = target.local;
auto options = Database::get_irc_server_options(owner.local + "@" + owner.domain,
server_domain);
+ auto commands = Database::get_after_connection_commands(options);
+
for (const XmlNode* field: x->get_children("field", "jabber:x:data"))
{
const XmlNode* value = field->get_child("value", "jabber:x:data");
const std::vector<const XmlNode*> values = field->get_children("value", "jabber:x:data");
+
+ if (field->get_tag("var") == "address" && value)
+ options.col<Database::Address>() = value->get_inner();
+
if (field->get_tag("var") == "ports")
{
std::string ports;
@@ -406,11 +441,22 @@ void ConfigureIrcServerStep2(XmppComponent&, AdhocSession& session, XmlNode& com
#endif // BOTAN_FOUND
+ else if (field->get_tag("var") == "nick" && value)
+ options.col<Database::Nick>() = value->get_inner();
+
else if (field->get_tag("var") == "pass" && value)
options.col<Database::Pass>() = value->get_inner();
- else if (field->get_tag("var") == "after_connect_command" && value)
- options.col<Database::AfterConnectionCommand>() = value->get_inner();
+ else if (field->get_tag("var") == "after_connect_commands")
+ {
+ commands.clear();
+ for (const auto& val: values)
+ {
+ auto command = Database::after_connection_commands.row();
+ command.col<Database::AfterConnectionCommand>() = val->get_inner();
+ commands.push_back(std::move(command));
+ }
+ }
else if (field->get_tag("var") == "username" && value)
{
@@ -431,7 +477,8 @@ void ConfigureIrcServerStep2(XmppComponent&, AdhocSession& session, XmlNode& com
}
Database::invalidate_encoding_in_cache();
- options.save(Database::db);
+ save(options, *Database::db);
+ Database::set_after_connection_commands(options, commands);
command_node.delete_all_children();
XmlSubNode note(command_node, "note");
@@ -600,7 +647,7 @@ bool handle_irc_channel_configuration_form(XmppComponent& xmpp_component, const
}
Database::invalidate_encoding_in_cache(requester.bare(), iid.get_server(), iid.get_local());
- options.save(Database::db);
+ save(options, *Database::db);
}
return true;
}
@@ -611,7 +658,7 @@ bool handle_irc_channel_configuration_form(XmppComponent& xmpp_component, const
void DisconnectUserFromServerStep1(XmppComponent& xmpp_component, AdhocSession& session, XmlNode& command_node)
{
const Jid owner(session.get_owner_jid());
- if (owner.bare() != Config::get("admin", ""))
+ if (!Config::is_in_list("admin", owner.bare()))
{ // A non-admin is not allowed to disconnect other users, only
// him/herself, so we just skip this step
auto next_step = session.get_next_step();
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()))
diff --git a/src/xmpp/xmpp_component.cpp b/src/xmpp/xmpp_component.cpp
index 8f6826e..b3d925e 100644
--- a/src/xmpp/xmpp_component.cpp
+++ b/src/xmpp/xmpp_component.cpp
@@ -2,6 +2,7 @@
#include <utils/scopeguard.hpp>
#include <utils/tolower.hpp>
#include <logger/logger.hpp>
+#include <utils/uuid.hpp>
#include <xmpp/xmpp_component.hpp>
#include <config/config.hpp>
@@ -14,8 +15,6 @@
#include <iostream>
#include <set>
-#include <uuid/uuid.h>
-
#include <cstdlib>
#include <set>
@@ -364,10 +363,11 @@ void XmppComponent::send_topic(const std::string& from, Xmpp::body&& topic, cons
this->send_stanza(message);
}
-void XmppComponent::send_muc_message(const std::string& muc_name, const std::string& nick, Xmpp::body&& xmpp_body, const std::string& jid_to, std::string uuid)
+void XmppComponent::send_muc_message(const std::string& muc_name, const std::string& nick, Xmpp::body&& xmpp_body, const std::string& jid_to, std::string uuid, std::string id)
{
Stanza message("message");
message["to"] = jid_to;
+ message["id"] = std::move(id);
if (!nick.empty())
message["from"] = muc_name + "@" + this->served_hostname + "/" + nick;
else // Message from the room itself
@@ -398,7 +398,8 @@ void XmppComponent::send_muc_message(const std::string& muc_name, const std::str
this->send_stanza(message);
}
-void XmppComponent::send_history_message(const std::string& muc_name, const std::string& nick, const std::string& body_txt, const std::string& jid_to, std::time_t timestamp)
+#ifdef USE_DATABASE
+void XmppComponent::send_history_message(const std::string& muc_name, const std::string& nick, const std::string& body_txt, const std::string& jid_to, Database::time_point::rep timestamp)
{
Stanza message("message");
message["to"] = jid_to;
@@ -421,9 +422,11 @@ void XmppComponent::send_history_message(const std::string& muc_name, const std:
this->send_stanza(message);
}
+#endif
void XmppComponent::send_muc_leave(const std::string& muc_name, const std::string& nick, Xmpp::body&& message,
- const std::string& jid_to, const bool self, const bool user_requested)
+ const std::string& jid_to, const bool self, const bool user_requested,
+ const std::string& affiliation, const std::string& role)
{
Stanza presence("presence");
{
@@ -445,6 +448,9 @@ void XmppComponent::send_muc_leave(const std::string& muc_name, const std::strin
status["code"] = "332";
}
}
+ XmlSubNode item(x, "item");
+ item["affiliation"] = affiliation;
+ item["role"] = role;
if (!message_str.empty())
{
XmlSubNode status(presence, "status");
@@ -667,9 +673,5 @@ void XmppComponent::send_iq_result(const std::string& id, const std::string& to_
std::string XmppComponent::next_id()
{
- char uuid_str[37];
- uuid_t uuid;
- uuid_generate(uuid);
- uuid_unparse(uuid, uuid_str);
- return uuid_str;
+ return utils::gen_uuid();
}
diff --git a/src/xmpp/xmpp_component.hpp b/src/xmpp/xmpp_component.hpp
index 3950863..e18da40 100644
--- a/src/xmpp/xmpp_component.hpp
+++ b/src/xmpp/xmpp_component.hpp
@@ -1,8 +1,10 @@
#pragma once
+#include "biboumi.h"
#include <xmpp/adhoc_commands_handler.hpp>
#include <network/tcp_client_socket_handler.hpp>
+#include <database/database.hpp>
#include <xmpp/xmpp_parser.hpp>
#include <xmpp/body.hpp>
@@ -35,6 +37,7 @@
#define RSM_NS "http://jabber.org/protocol/rsm"
#define MUC_TRAFFIC_NS "http://jabber.org/protocol/muc#traffic"
#define STABLE_ID_NS "urn:xmpp:sid:0"
+#define STABLE_MUC_ID_NS "http://jabber.org/protocol/muc#stable_id"
/**
* An XMPP component, communicating with an XMPP server using the protocole
@@ -132,12 +135,14 @@ public:
* Send a (non-private) message to the MUC
*/
void send_muc_message(const std::string& muc_name, const std::string& nick, Xmpp::body&& body, const std::string& jid_to,
- std::string uuid);
+ std::string uuid, std::string id);
+#ifdef USE_DATABASE
/**
* Send a message, with a <delay/> element, part of a MUC history
*/
void send_history_message(const std::string& muc_name, const std::string& nick, const std::string& body,
- const std::string& jid_to, const std::time_t timestamp);
+ const std::string& jid_to, Database::time_point::rep timestamp);
+#endif
/**
* Send an unavailable presence for this nick
*/
@@ -146,7 +151,8 @@ public:
Xmpp::body&& message,
const std::string& jid_to,
const bool self,
- const bool user_requested);
+ const bool user_requested,
+ const std::string& affiliation, const std::string& role);
/**
* Indicate that a participant changed his nick
*/