diff options
Diffstat (limited to 'src/xmpp')
-rw-r--r-- | src/xmpp/adhoc_commands_handler.cpp | 2 | ||||
-rw-r--r-- | src/xmpp/biboumi_adhoc_commands.cpp | 10 | ||||
-rw-r--r-- | src/xmpp/biboumi_component.cpp | 101 | ||||
-rw-r--r-- | src/xmpp/body.hpp | 4 | ||||
-rw-r--r-- | src/xmpp/xmpp_component.cpp | 23 | ||||
-rw-r--r-- | src/xmpp/xmpp_component.hpp | 11 |
6 files changed, 123 insertions, 28 deletions
diff --git a/src/xmpp/adhoc_commands_handler.cpp b/src/xmpp/adhoc_commands_handler.cpp index e4dcd5c..bb48781 100644 --- a/src/xmpp/adhoc_commands_handler.cpp +++ b/src/xmpp/adhoc_commands_handler.cpp @@ -83,7 +83,7 @@ XmlNode AdhocCommandsHandler::handle_request(const std::string& executor_jid, co XmlSubNode next(actions, "next"); } } - else if (action == "cancel") + else if (session_it != this->sessions.end() && action == "cancel") { this->sessions.erase(session_it); command_node["status"] = "canceled"; diff --git a/src/xmpp/biboumi_adhoc_commands.cpp b/src/xmpp/biboumi_adhoc_commands.cpp index 60af506..bcdac39 100644 --- a/src/xmpp/biboumi_adhoc_commands.cpp +++ b/src/xmpp/biboumi_adhoc_commands.cpp @@ -159,7 +159,7 @@ void ConfigureGlobalStep1(XmppComponent&, AdhocSession& session, XmlNode& comman { XmlSubNode value(persistent, "value"); value.set_name("value"); - if (options.col<Database::Persistent>()) + if (options.col<Database::GlobalPersistent>()) value.set_inner("true"); else value.set_inner("false"); @@ -193,7 +193,7 @@ void ConfigureGlobalStep2(XmppComponent& xmpp_component, AdhocSession& session, } else if (field->get_tag("var") == "persistent" && value) - options.col<Database::Persistent>() = to_bool(value->get_inner()); + options.col<Database::GlobalPersistent>() = to_bool(value->get_inner()); } options.save(Database::db); @@ -409,7 +409,7 @@ void ConfigureIrcServerStep2(XmppComponent&, AdhocSession& session, XmlNode& com else if (field->get_tag("var") == "pass" && value) options.col<Database::Pass>() = value->get_inner(); - else if (field->get_tag("var") == "after_connect_command") + else if (field->get_tag("var") == "after_connect_command" && value) options.col<Database::AfterConnectionCommand>() = value->get_inner(); else if (field->get_tag("var") == "username" && value) @@ -430,7 +430,7 @@ void ConfigureIrcServerStep2(XmppComponent&, AdhocSession& session, XmlNode& com options.col<Database::EncodingIn>() = value->get_inner(); } - + Database::invalidate_encoding_in_cache(); options.save(Database::db); command_node.delete_all_children(); @@ -599,7 +599,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); } return true; diff --git a/src/xmpp/biboumi_component.cpp b/src/xmpp/biboumi_component.cpp index 0e1d270..481ebb9 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> @@ -24,6 +25,7 @@ #include <database/database.hpp> #include <bridge/result_set_management.hpp> +#include <bridge/history_limit.hpp> using namespace std::string_literals; @@ -155,8 +157,32 @@ void BiboumiComponent::handle_presence(const Stanza& stanza) 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; + HistoryLimit history_limit; + if (history) + { + 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); + from.resource, history_limit); } else if (type == "unavailable") { @@ -281,6 +307,7 @@ void BiboumiComponent::handle_message(const Stanza& stanza) { if (body && !body->get_inner().empty()) { + const auto fixed_irc_server = Config::get("fixed_irc_server", ""); // a message for nick!server if (iid.type == Iid::Type::User && !iid.get_local().empty()) { @@ -296,9 +323,11 @@ void BiboumiComponent::handle_message(const Stanza& stanza) bridge->set_preferred_from_jid(user_iid.get_local(), to_str); } else if (iid.type == Iid::Type::Server) + bridge->send_raw_message(iid.get_server(), body->get_inner()); + else if (iid.type == Iid::Type::None && !fixed_irc_server.empty()) { // Message sent to the server JID // Convert the message body into a raw IRC message - bridge->send_raw_message(iid.get_server(), body->get_inner()); + bridge->send_raw_message(fixed_irc_server, body->get_inner()); } } } @@ -408,7 +437,7 @@ void BiboumiComponent::handle_iq(const Stanza& stanza) // Depending on the 'to' jid in the request, we use one adhoc // command handler or an other - Iid iid(to.local, {}); + Iid iid(to.local, {'#', '&'}); AdhocCommandsHandler* adhoc_handler; if (to.local.empty()) adhoc_handler = &this->adhoc_commands_handler; @@ -416,8 +445,13 @@ void BiboumiComponent::handle_iq(const Stanza& stanza) { if (iid.type == Iid::Type::Server) adhoc_handler = &this->irc_server_adhoc_commands_handler; - else + else if (iid.type == Iid::Type::Channel && to.resource.empty()) adhoc_handler = &this->irc_channel_adhoc_commands_handler; + else + { + error_name = "feature-not-implemented"; + return; + } } // Execute the command, if any, and get a result XmlNode that we // insert in our response @@ -467,7 +501,7 @@ void BiboumiComponent::handle_iq(const Stanza& stanza) stanza_error.disable(); } } - else if (iid.type == Iid::Type::Channel) + else if (iid.type == Iid::Type::Channel && to.resource.empty()) { if (node.empty()) { @@ -526,7 +560,7 @@ void BiboumiComponent::handle_iq(const Stanza& stanza) this->irc_server_adhoc_commands_handler); stanza_error.disable(); } - else if (iid.type == Iid::Type::Channel) + 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", "") == @@ -534,6 +568,8 @@ void BiboumiComponent::handle_iq(const Stanza& stanza) this->irc_channel_adhoc_commands_handler); stanza_error.disable(); } + else // “to” is a MUC user, not the room itself + error_name = "feature-not-implemented"; } else if (node.empty() && iid.type == Iid::Type::Server) { // Disco on an IRC server: get the list of channels @@ -685,19 +721,46 @@ bool BiboumiComponent::handle_mam_request(const Stanza& stanza) if (max) limit = std::atoi(max->get_inner().data()); } - // 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 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); + 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); } - this->send_iq_result_full_jid(id, from.full(), to.full()); + { + 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()) + { + XmlSubNode first(set, "first"); + first["index"] = "0"; + first.set_inner(lines[0].col<Database::Uuid>()); + XmlSubNode last(set, "last"); + last.set_inner(lines[lines.size() - 1].col<Database::Uuid>()); + } + } + this->send_iq_result_full_jid(id, from.full(), to.full(), std::move(fin_ptr)); + } return true; } return false; @@ -739,7 +802,7 @@ bool BiboumiComponent::handle_room_configuration_form_request(const std::string& { Iid iid(to.local, {'#', '&'}); - if (iid.type != Iid::Type::Channel) + if (iid.type != Iid::Type::Channel || !to.resource.empty()) return false; Stanza iq("iq"); @@ -761,7 +824,7 @@ bool BiboumiComponent::handle_room_configuration_form(const XmlNode& query, cons { Iid iid(to.local, {'#', '&'}); - if (iid.type != Iid::Type::Channel) + if (iid.type != Iid::Type::Channel || !to.resource.empty()) return false; Jid requester(from); @@ -958,7 +1021,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())) @@ -1052,6 +1117,9 @@ void BiboumiComponent::on_irc_client_connected(const std::string& irc_hostname, const auto local_jid = irc_hostname + "@" + this->served_hostname; if (Database::has_roster_item(local_jid, jid)) this->send_presence_to_contact(local_jid, jid, ""); +#else + (void)irc_hostname; + (void)jid; #endif } @@ -1061,6 +1129,9 @@ void BiboumiComponent::on_irc_client_disconnected(const std::string& irc_hostnam const auto local_jid = irc_hostname + "@" + this->served_hostname; if (Database::has_roster_item(local_jid, jid)) this->send_presence_to_contact(irc_hostname + "@" + this->served_hostname, jid, "unavailable"); +#else + (void)irc_hostname; + (void)jid; #endif } diff --git a/src/xmpp/body.hpp b/src/xmpp/body.hpp index 068d1a4..f693cdd 100644 --- a/src/xmpp/body.hpp +++ b/src/xmpp/body.hpp @@ -1,5 +1,9 @@ #pragma once +#include <tuple> +#include <memory> + +class XmlNode; namespace Xmpp { diff --git a/src/xmpp/xmpp_component.cpp b/src/xmpp/xmpp_component.cpp index 42a5392..9be9e34 100644 --- a/src/xmpp/xmpp_component.cpp +++ b/src/xmpp/xmpp_component.cpp @@ -269,7 +269,8 @@ void* XmppComponent::get_receive_buffer(const size_t size) const } void XmppComponent::send_message(const std::string& from, Xmpp::body&& body, const std::string& to, - const std::string& type, const bool fulljid, const bool nocopy) + const std::string& type, const bool fulljid, const bool nocopy, + const bool muc_private) { Stanza message("message"); { @@ -277,7 +278,12 @@ void XmppComponent::send_message(const std::string& from, Xmpp::body&& body, con if (fulljid) message["from"] = from; else - message["from"] = from + "@" + this->served_hostname; + { + if (!from.empty()) + message["from"] = from + "@" + this->served_hostname; + else + message["from"] = this->served_hostname; + } if (!type.empty()) message["type"] = type; XmlSubNode body_node(message, "body"); @@ -296,6 +302,11 @@ void XmppComponent::send_message(const std::string& from, Xmpp::body&& body, con XmlSubNode nocopy(message, "no-copy"); nocopy["xmlns"] = "urn:xmpp:hints"; } + if (muc_private) + { + XmlSubNode x(message, "x"); + x["xmlns"] = MUC_USER_NS; + } } this->send_stanza(message); } @@ -387,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; @@ -410,6 +422,7 @@ 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) @@ -629,13 +642,15 @@ void XmppComponent::send_iq_version_request(const std::string& from, this->send_stanza(iq); } -void XmppComponent::send_iq_result_full_jid(const std::string& id, const std::string& to_jid, const std::string& from_full_jid) +void XmppComponent::send_iq_result_full_jid(const std::string& id, const std::string& to_jid, const std::string& from_full_jid, std::unique_ptr<XmlNode> inner) { Stanza iq("iq"); iq["from"] = from_full_jid; iq["to"] = to_jid; iq["id"] = id; iq["type"] = "result"; + if (inner) + iq.add_child(std::move(inner)); this->send_stanza(iq); } diff --git a/src/xmpp/xmpp_component.hpp b/src/xmpp/xmpp_component.hpp index 22d5c48..1daa6fb 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> @@ -112,7 +114,8 @@ public: * server-part of the JID and must be added. */ void send_message(const std::string& from, Xmpp::body&& body, const std::string& to, - const std::string& type, const bool fulljid, const bool nocopy=false); + const std::string& type, const bool fulljid, const bool nocopy=false, + const bool muc_private=false); /** * Send a join from a new participant */ @@ -132,11 +135,13 @@ public: */ void send_muc_message(const std::string& muc_name, const std::string& nick, Xmpp::body&& body, const std::string& jid_to, std::string uuid); +#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 */ @@ -202,7 +207,7 @@ public: */ void send_iq_result(const std::string& id, const std::string& to_jid, const std::string& from); void send_iq_result_full_jid(const std::string& id, const std::string& to_jid, - const std::string& from_full_jid); + const std::string& from_full_jid, std::unique_ptr<XmlNode> inner=nullptr); void handle_handshake(const Stanza& stanza); void handle_error(const Stanza& stanza); |