Merge branch 'user/luca/iq-gateway' of https://codeberg.org/aninternettroll/biboumi
* 'user/luca/iq-gateway' of https://codeberg.org/aninternettroll/biboumi: Add support for XEP-0100 jabber:iq:gateway
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
#cmakedefine USE_DATABASE
|
||||
#cmakedefine ICONV_SECOND_ARGUMENT_IS_CONST
|
||||
#cmakedefine LIBIDN_FOUND
|
||||
#cmakedefine CURL_FOUND
|
||||
#cmakedefine SYSTEMD_FOUND
|
||||
#cmakedefine POLLER ${POLLER}
|
||||
#cmakedefine BOTAN_FOUND
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
#include <irc/irc_uri.hpp>
|
||||
|
||||
#include <utils/split.hpp>
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
|
||||
|
||||
IrcUri::IrcUri(const std::string &uri) {
|
||||
#ifdef CURL_FOUND
|
||||
this->url = curl_url();
|
||||
curl_url_set(this->url, CURLUPART_URL, uri.c_str(), CURLU_NON_SUPPORT_SCHEME | CURLU_NO_AUTHORITY);
|
||||
#endif
|
||||
(void)uri;
|
||||
}
|
||||
|
||||
IrcUri::~IrcUri(void) {
|
||||
#ifdef CURL_FOUND
|
||||
curl_url_cleanup(this->url);
|
||||
#endif
|
||||
}
|
||||
|
||||
const std::optional<std::string> IrcUri::get_server(void) const {
|
||||
#ifdef CURL_FOUND
|
||||
char *server;
|
||||
CURLUcode rc = curl_url_get(this->url, CURLUPART_HOST, &server, 0);
|
||||
if (rc) {
|
||||
return std::nullopt;
|
||||
}
|
||||
return server;
|
||||
#else
|
||||
return std::nullopt;
|
||||
#endif // CURL_FOUND
|
||||
};
|
||||
|
||||
const std::optional<std::string> IrcUri::get_entity(void) const {
|
||||
#ifdef CURL_FOUND
|
||||
char *entity;
|
||||
std::string output;
|
||||
CURLUcode rc;
|
||||
rc = curl_url_get(this->url, CURLUPART_PATH, &entity, CURLU_URLDECODE);
|
||||
if (rc)
|
||||
return std::nullopt;
|
||||
output = entity;
|
||||
if (!output.empty())
|
||||
output.erase(0, 1);
|
||||
if (output.empty()) {
|
||||
rc = curl_url_get(this->url, CURLUPART_FRAGMENT, &entity, CURLU_URLDECODE);
|
||||
if (rc)
|
||||
return std::nullopt;
|
||||
output = entity;
|
||||
output.insert(0, 1, '#');
|
||||
}
|
||||
return IrcUri::parse_entity(output);
|
||||
#else
|
||||
return std::nullopt;
|
||||
#endif // CURL_FOUND
|
||||
};
|
||||
|
||||
const std::string IrcUri::parse_entity(const std::string entity) {
|
||||
const std::vector<std::string> entity_name_and_type =
|
||||
utils::split(entity, ',');
|
||||
|
||||
if (entity_name_and_type.size() > 0)
|
||||
return entity_name_and_type.at(0);
|
||||
|
||||
return entity;
|
||||
};
|
||||
@@ -0,0 +1,37 @@
|
||||
#pragma once
|
||||
|
||||
#include <biboumi.h>
|
||||
|
||||
#include <optional>
|
||||
#include <xmpp/jid.hpp>
|
||||
#include <string>
|
||||
#include <curl/urlapi.h>
|
||||
|
||||
/**
|
||||
* Parses and keeps information about one IRC URI
|
||||
*
|
||||
* https://datatracker.ietf.org/doc/html/draft-butcher-irc-url-04
|
||||
*
|
||||
* Requires CURL
|
||||
*/
|
||||
class IrcUri
|
||||
{
|
||||
public:
|
||||
explicit IrcUri(const std::string &uri);
|
||||
~IrcUri(void);
|
||||
|
||||
IrcUri(const IrcUri&) = delete;
|
||||
IrcUri(IrcUri&&) = delete;
|
||||
IrcUri& operator=(const IrcUri&) = delete;
|
||||
IrcUri& operator=(IrcUri&&) = delete;
|
||||
|
||||
const std::optional<std::string> get_server(void) const;
|
||||
const std::optional<std::string> get_entity(void) const;
|
||||
|
||||
private:
|
||||
#ifdef CURL_FOUND
|
||||
CURLU *url;
|
||||
#endif
|
||||
|
||||
static const std::string parse_entity(const std::string entity);
|
||||
};
|
||||
@@ -1,18 +1,23 @@
|
||||
#include <xmpp/hash_disco.hpp>
|
||||
#include <xmpp/biboumi_component.hpp>
|
||||
|
||||
#include <utils/empty_if_fixed_server.hpp>
|
||||
#include <utils/timed_events.hpp>
|
||||
#include <utils/scopeguard.hpp>
|
||||
#include <utils/tolower.hpp>
|
||||
#include <logger/logger.hpp>
|
||||
#include <xmpp/adhoc_command.hpp>
|
||||
#include <xmpp/biboumi_adhoc_commands.hpp>
|
||||
#include <xmpp/xmpp_component.hpp>
|
||||
#include <bridge/list_element.hpp>
|
||||
#include <utils/encoding.hpp>
|
||||
#include <config/config.hpp>
|
||||
#include <utils/time.hpp>
|
||||
#include <xmpp/jid.hpp>
|
||||
#include <irc/irc_uri.hpp>
|
||||
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <stdexcept>
|
||||
|
||||
#include <cstdlib>
|
||||
@@ -449,9 +454,10 @@ void BiboumiComponent::handle_iq(const Stanza& stanza)
|
||||
// the scopeguard.
|
||||
std::string error_type("cancel");
|
||||
std::string error_name("internal-server-error");
|
||||
utils::ScopeGuard stanza_error([this, &from, &to_str, &id, &error_type, &error_name](){
|
||||
std::string error_text("");
|
||||
utils::ScopeGuard stanza_error([this, &from, &to_str, &id, &error_type, &error_name, &error_text](){
|
||||
this->send_stanza_error("iq", from, to_str, id,
|
||||
error_type, error_name, "");
|
||||
error_type, error_name, error_text);
|
||||
});
|
||||
try {
|
||||
if (type == "set")
|
||||
@@ -518,6 +524,21 @@ void BiboumiComponent::handle_iq(const Stanza& stanza)
|
||||
this->send_stanza(response);
|
||||
stanza_error.disable();
|
||||
}
|
||||
#ifdef CURL_FOUND
|
||||
else if ((query = stanza.get_child("query", GATEWAY_NS))) {
|
||||
std::optional<Stanza> stanza =
|
||||
this->handle_iq_gateway(query, &to, from, id);
|
||||
if (stanza.has_value()) {
|
||||
this->send_stanza(stanza.value());
|
||||
stanza_error.disable();
|
||||
} else {
|
||||
error_name = "bad-request";
|
||||
error_type = "modify";
|
||||
error_text = "Invalid input. Check that the provided link is a valid irc URI";
|
||||
return;
|
||||
}
|
||||
}
|
||||
#endif // CURL_FOUND
|
||||
#ifdef USE_DATABASE
|
||||
else if ((query = stanza.get_child("query", MAM_NS)))
|
||||
{
|
||||
@@ -674,6 +695,28 @@ void BiboumiComponent::handle_iq(const Stanza& stanza)
|
||||
}
|
||||
stanza_error.disable();
|
||||
}
|
||||
#ifdef CURL_FOUND
|
||||
else if ((query = stanza.get_child("query", GATEWAY_NS)))
|
||||
{
|
||||
Stanza iq("iq");
|
||||
{
|
||||
iq["from"] = to_str;
|
||||
iq["to"] = from;
|
||||
iq["id"] = id;
|
||||
iq["type"] = "result";
|
||||
XmlSubNode query(iq, GATEWAY_NS, "query");
|
||||
{
|
||||
query["xmlns"] = GATEWAY_NS;
|
||||
XmlSubNode desc(query, "desc");
|
||||
desc.set_inner("Enter an IRC URI. For example "
|
||||
"irc://irc.example.com/#channame");
|
||||
XmlSubNode prompt(query, "prompt");
|
||||
prompt.set_inner("IRC URI");
|
||||
};
|
||||
}
|
||||
this->send_stanza(iq);
|
||||
}
|
||||
#endif // CURL_FOUND
|
||||
#ifdef USE_DATABASE
|
||||
else if ((query = stanza.get_child("query", MUC_OWNER_NS)))
|
||||
{
|
||||
@@ -737,6 +780,50 @@ void BiboumiComponent::handle_iq(const Stanza& stanza)
|
||||
error_name = "feature-not-implemented";
|
||||
}
|
||||
|
||||
#ifdef CURL_FOUND
|
||||
std::optional<Stanza>
|
||||
BiboumiComponent::handle_iq_gateway(const Stanza *stanza, const Jid *from,
|
||||
const std::string to,
|
||||
const std::string id) {
|
||||
const XmlNode *prompt = stanza->get_child("prompt", GATEWAY_NS);
|
||||
const std::string inner = prompt->get_inner();
|
||||
|
||||
std::string local;
|
||||
bool is_irc_uri =
|
||||
(inner.starts_with("irc://") || inner.starts_with("ircs://"));
|
||||
if (is_irc_uri) {
|
||||
IrcUri irc_uri(inner);
|
||||
const std::optional<std::string> entity = irc_uri.get_entity();
|
||||
const std::optional<std::string> server = irc_uri.get_server();
|
||||
const std::string irc_server = utils::empty_if_fixed_server("%" + server.value_or(""));
|
||||
if (irc_server != "%")
|
||||
local = entity.value_or("") + irc_server;
|
||||
} else {
|
||||
Iid i(inner, this->get_user_bridge(to));
|
||||
const std::string iid_local = i.get_local();
|
||||
if (!iid_local.empty())
|
||||
local = iid_local + utils::empty_if_fixed_server("%" + i.get_server());
|
||||
}
|
||||
if (local.empty())
|
||||
return std::nullopt;
|
||||
Jid j(local + "@" + from->domain);
|
||||
Stanza iq("iq");
|
||||
{
|
||||
iq["from"] = from->full();
|
||||
iq["to"] = to;
|
||||
iq["id"] = id;
|
||||
iq["type"] = "result";
|
||||
XmlSubNode query(iq, GATEWAY_NS, "query");
|
||||
{
|
||||
query["xmlns"] = GATEWAY_NS;
|
||||
XmlSubNode jid(query, "jid");
|
||||
jid.set_inner(j.full());
|
||||
};
|
||||
}
|
||||
return iq;
|
||||
}
|
||||
#endif // CURL_FOUND
|
||||
|
||||
#ifdef USE_DATABASE
|
||||
bool BiboumiComponent::handle_mam_request(const Stanza& stanza)
|
||||
{
|
||||
|
||||
@@ -93,6 +93,9 @@ private:
|
||||
void send_invitation_from_fulljid(const std::string& room_target, const std::string& jid_to, const std::string& from);
|
||||
static Stanza get_self_disco_info(const std::string &node);
|
||||
static std::string get_self_ver() { return Xmpp::hash_disco_sha1(get_self_disco_info("")); };
|
||||
#ifdef CURL_FOUND
|
||||
std::optional<Stanza> handle_iq_gateway(const Stanza *stanza, const Jid *from, const std::string to, const std::string id);
|
||||
#endif
|
||||
public:
|
||||
void accept_subscription(const std::string& from, const std::string& to);
|
||||
void ask_subscription(const std::string& from, const std::string& to);
|
||||
|
||||
@@ -42,6 +42,7 @@
|
||||
#define STABLE_MUC_ID_NS "http://jabber.org/protocol/muc#stable_id"
|
||||
#define SELF_PING_FLAG MUC_NS"#self-ping-optimization"
|
||||
#define MESSAGE_CORRECT_NS "urn:xmpp:message-correct:0"
|
||||
#define GATEWAY_NS "jabber:iq:gateway"
|
||||
|
||||
/**
|
||||
* An XMPP component, communicating with an XMPP server using the protocole
|
||||
|
||||
Reference in New Issue
Block a user