diff options
-rw-r--r-- | README.rst | 1 | ||||
-rw-r--r-- | louloulibs/xmpp/xmpp_component.hpp | 1 | ||||
-rw-r--r-- | src/database/database.cpp | 32 | ||||
-rw-r--r-- | src/database/database.hpp | 2 | ||||
-rw-r--r-- | src/xmpp/biboumi_component.cpp | 37 | ||||
-rw-r--r-- | tests/end_to_end/__main__.py | 26 |
6 files changed, 82 insertions, 17 deletions
@@ -29,6 +29,7 @@ Usage ----- Read `the documentation`_. + Authors ------- Florent Le Coz (louiz’) <louiz@louiz.org> diff --git a/louloulibs/xmpp/xmpp_component.hpp b/louloulibs/xmpp/xmpp_component.hpp index 8359d05..b556ce2 100644 --- a/louloulibs/xmpp/xmpp_component.hpp +++ b/louloulibs/xmpp/xmpp_component.hpp @@ -30,6 +30,7 @@ #define MAM_NS "urn:xmpp:mam:1" #define FORWARD_NS "urn:xmpp:forward:0" #define CLIENT_NS "jabber:client" +#define DATAFORM_NS "jabber:x:data" /** * An XMPP component, communicating with an XMPP server using the protocole diff --git a/src/database/database.cpp b/src/database/database.cpp index be0da8e..e995d95 100644 --- a/src/database/database.cpp +++ b/src/database/database.cpp @@ -6,6 +6,7 @@ #include <irc/iid.hpp> #include <uuid.h> #include <utils/get_first_non_empty.hpp> +#include <utils/time.hpp> using namespace std::string_literals; @@ -136,14 +137,30 @@ void Database::store_muc_message(const std::string& owner, const Iid& iid, line.update(); } -std::vector<db::MucLogLine> Database::get_muc_logs(const std::string& owner, const std::string& chan_name, const std::string& server, int limit) +std::vector<db::MucLogLine> Database::get_muc_logs(const std::string& owner, const std::string& chan_name, const std::string& server, + int limit, const std::string& start, const std::string& end) { - if (limit == -1) - limit = 1024; - const auto& res = litesql::select<db::MucLogLine>(*Database::db, - db::MucLogLine::Owner == owner && - db::MucLogLine::IrcChanName == chan_name && - db::MucLogLine::IrcServerName == server).orderBy(db::MucLogLine::Id, false).limit(limit).all(); + auto request = litesql::select<db::MucLogLine>(*Database::db, + db::MucLogLine::Owner == owner && + db::MucLogLine::IrcChanName == chan_name && + db::MucLogLine::IrcServerName == server); + request.orderBy(db::MucLogLine::Id, false); + + if (limit >= 0) + request.limit(limit); + if (!start.empty()) + { + const auto start_time = utils::parse_datetime(start); + if (start_time != -1) + request.where(db::MucLogLine::Date >= start_time); + } + if (!end.empty()) + { + const auto end_time = utils::parse_datetime(end); + if (end_time != -1) + request.where(db::MucLogLine::Date <= end_time); + } + const auto& res = request.all(); return {res.crbegin(), res.crend()}; } @@ -152,7 +169,6 @@ void Database::close() Database::db.reset(nullptr); } - std::string Database::gen_uuid() { char uuid_str[37]; diff --git a/src/database/database.hpp b/src/database/database.hpp index e7fdd5f..6823574 100644 --- a/src/database/database.hpp +++ b/src/database/database.hpp @@ -49,7 +49,7 @@ public: const std::string& server, const std::string& channel); static std::vector<db::MucLogLine> get_muc_logs(const std::string& owner, const std::string& chan_name, const std::string& server, - int limit); + int limit=-1, const std::string& before="", const std::string& after=""); static void store_muc_message(const std::string& owner, const Iid& iid, time_point date, const std::string& body, const std::string& nick); diff --git a/src/xmpp/biboumi_component.cpp b/src/xmpp/biboumi_component.cpp index a49f52e..a2fda60 100644 --- a/src/xmpp/biboumi_component.cpp +++ b/src/xmpp/biboumi_component.cpp @@ -550,13 +550,36 @@ bool BiboumiComponent::handle_mam_request(const Stanza& stanza) Iid iid(to.local, {'#', '&'}); if (iid.type == Iid::Type::Channel && to.resource.empty()) { - const auto lines = Database::get_muc_logs(from.bare(), iid.get_local(), iid.get_server(), -1); - for (const db::MucLogLine& line: lines) - { - const auto queryid = query->get_tag("queryid"); - if (!line.nick.value().empty()) - this->send_archived_message(line, to.full(), from.full(), queryid); - } + std::string start; + std::string end; + const XmlNode* x = query->get_child("x", DATAFORM_NS); + if (x) + { + const XmlNode* value; + const auto fields = x->get_children("field", DATAFORM_NS); + for (const auto& field: fields) + { + if (field->get_tag("var") == "start") + { + value = field->get_child("value", DATAFORM_NS); + if (value) + start = value->get_inner(); + } + else if (field->get_tag("var") == "end") + { + value = field->get_child("value", DATAFORM_NS); + if (value) + end = value->get_inner(); + } + } + } + const auto lines = Database::get_muc_logs(from.bare(), iid.get_local(), iid.get_server(), -1, start, end); + for (const db::MucLogLine& line: lines) + { + const auto queryid = query->get_tag("queryid"); + if (!line.nick.value().empty()) + this->send_archived_message(line, to.full(), from.full(), queryid); + } this->send_iq_result_full_jid(id, from.full(), to.full()); return true; } diff --git a/tests/end_to_end/__main__.py b/tests/end_to_end/__main__.py index 57e91c9..e1779de 100644 --- a/tests/end_to_end/__main__.py +++ b/tests/end_to_end/__main__.py @@ -1086,8 +1086,32 @@ if __name__ == '__main__': ), partial(expect_stanza, - "/iq[@type='result'][@id='id1'][@from='#foo%{irc_server_one}'][@to='{jid_one}/{resource_one}']") + "/iq[@type='result'][@id='id1'][@from='#foo%{irc_server_one}'][@to='{jid_one}/{resource_one}']"), + # Retrieve an empty archive by specifying an early “end” date + partial(send_stanza, """<iq to='#foo%{irc_server_one}' from='{jid_one}/{resource_one}' type='set' id='id2'> + <query xmlns='urn:xmpp:mam:1' queryid='qid2'> + <x xmlns='jabber:x:data' type='submit'> + <field var='FORM_TYPE' type='hidden'> <value>urn:xmpp:mam:1</value></field> + <field var='end'><value>2000-06-07T00:00:00Z</value></field> + </x> + </query></iq>"""), + + partial(expect_stanza, + "/iq[@type='result'][@id='id2'][@from='#foo%{irc_server_one}'][@to='{jid_one}/{resource_one}']"), + + # Retrieve an empty archive by specifying a late “start” date + # (note that this test will break in ~1000 years) + partial(send_stanza, """<iq to='#foo%{irc_server_one}' from='{jid_one}/{resource_one}' type='set' id='id3'> + <query xmlns='urn:xmpp:mam:1' queryid='qid3'> + <x xmlns='jabber:x:data' type='submit'> + <field var='FORM_TYPE' type='hidden'> <value>urn:xmpp:mam:1</value></field> + <field var='start'><value>3016-06-07T00:00:00Z</value></field> + </x> + </query></iq>"""), + + partial(expect_stanza, + "/iq[@type='result'][@id='id3'][@from='#foo%{irc_server_one}'][@to='{jid_one}/{resource_one}']"), ]), ) |