/catalog/v2/entries going through OPDSDumper

OPDSDumper sensed threats to its job security, so it lobbied to be
involved in handling the /catalog/v2 endpoints, too.
This commit is contained in:
Veloman Yunkan 2021-05-24 12:17:15 +04:00
parent dfad1c3815
commit cdacc0caf1
6 changed files with 90 additions and 59 deletions

View File

@ -51,11 +51,26 @@ class OPDSDumper
/**
* Dump the OPDS feed.
*
* @param id The id of the library.
* @param bookIds the ids of the books to include in the feed
* @return The OPDS feed.
*/
std::string dumpOPDSFeed(const std::vector<std::string>& bookIds);
/**
* Dump the OPDS feed.
*
* @param bookIds the ids of the books to include in the feed
* @return The OPDS feed.
*/
std::string dumpOPDSFeedV2(const std::vector<std::string>& bookIds, const std::string& query) const;
/**
* Set the id of the library.
*
* @param id the id to use.
*/
void setLibraryId(const std::string& id) { this->libraryId = id;}
/**
* Set the id of the opds stream.
*
@ -103,6 +118,7 @@ class OPDSDumper
protected:
kiwix::Library* library;
std::string id;
std::string libraryId;
std::string title;
std::string date;
std::string rootLocation;

View File

@ -24,6 +24,7 @@
#include <vector>
#include <map>
#include <zim/zim.h>
#include <mustache.hpp>
namespace pugi {
class xml_node;
@ -47,6 +48,9 @@ namespace kiwix
MimeCounterType parseMimetypeCounter(const std::string& counterData);
std::string gen_date_str();
std::string gen_uuid(const std::string& s);
std::string render_template(const std::string& template_str, kainjow::mustache::data data);
}
#endif

View File

@ -22,6 +22,9 @@
#include "tools/otherTools.h"
#include "kiwixlib-resources.h"
#include <mustache.hpp>
namespace kiwix
{
/* Constructor */
@ -135,4 +138,45 @@ string OPDSDumper::dumpOPDSFeed(const std::vector<std::string>& bookIds)
return nodeToString(root_node);
}
typedef kainjow::mustache::data MustacheData;
string OPDSDumper::dumpOPDSFeedV2(const std::vector<std::string>& bookIds, const std::string& query) const
{
kainjow::mustache::list bookData;
for ( const auto& bookId : bookIds ) {
const Book& book = library->getBookById(bookId);
const MustacheData bookUrl = book.getUrl().empty()
? MustacheData(false)
: MustacheData(book.getUrl());
bookData.push_back(kainjow::mustache::object{
{"id", "urn:uuid:"+book.getId()},
{"name", book.getName()},
{"title", book.getTitle()},
{"description", book.getDescription()},
{"language", book.getLanguage()},
{"content_id", book.getHumanReadableIdFromPath()},
{"updated", book.getDate() + "T00:00:00Z"},
{"category", book.getCategory()},
{"flavour", book.getFlavour()},
{"tags", book.getTags()},
{"article_count", to_string(book.getArticleCount())},
{"media_count", to_string(book.getMediaCount())},
{"author_name", book.getCreator()},
{"publisher_name", book.getPublisher()},
{"url", bookUrl},
{"size", to_string(book.getSize())},
});
}
const kainjow::mustache::object template_data{
{"date", gen_date_str()},
{"endpoint_root", rootLocation + "/catalog/v2"},
{"feed_id", gen_uuid(libraryId + "/entries?"+query)},
{"filter", query.empty() ? MustacheData(false) : MustacheData(query)},
{"books", bookData }
};
return render_template(RESOURCE::catalog_v2_entries_xml, template_data);
}
}

View File

@ -79,11 +79,6 @@ namespace kiwix {
namespace
{
inline std::string gen_uuid(const std::string& s)
{
return to_string(zim::Uuid::generate(s));
}
inline std::string normalizeRootUrl(const std::string& rootUrl)
{
return (rootUrl.empty() || rootUrl[0] == '/')
@ -767,53 +762,17 @@ std::unique_ptr<Response> InternalServer::handle_catalog_v2_root(const RequestCo
std::unique_ptr<Response> InternalServer::handle_catalog_v2_entries(const RequestContext& request)
{
const std::string root_url = normalizeRootUrl(m_root);
const auto now = gen_date_str();
kainjow::mustache::list bookData;
const auto filter = get_search_filter(request);
const auto allMatchingEntries = mp_library->filter(filter);
const size_t count = request.get_optional_param("count", 10UL);
const size_t start = request.get_optional_param("start", 0UL);
for ( const auto& bookId : subrange(allMatchingEntries, start, count) ) {
const Book& book = mp_library->getBookById(bookId);
const MustacheData bookUrl = book.getUrl().empty()
? MustacheData(false)
: MustacheData(book.getUrl());
bookData.push_back(kainjow::mustache::object{
{"id", "urn:uuid:"+book.getId()},
{"name", book.getName()},
{"title", book.getTitle()},
{"description", book.getDescription()},
{"language", book.getLanguage()},
{"content_id", book.getHumanReadableIdFromPath()},
{"updated", book.getDate() + "T00:00:00Z"},
{"category", book.getCategory()},
{"flavour", book.getFlavour()},
{"tags", book.getTags()},
{"article_count", to_string(book.getArticleCount())},
{"media_count", to_string(book.getMediaCount())},
{"author_name", book.getCreator()},
{"publisher_name", book.getPublisher()},
{"url", bookUrl},
{"size", to_string(book.getSize())},
});
}
const auto query = request.get_query().empty()
? MustacheData(false)
: MustacheData(request.get_query());
const auto bookIds = subrange(mp_library->filter(filter), start, count);
OPDSDumper opdsDumper(mp_library);
opdsDumper.setRootLocation(normalizeRootUrl(m_root));
opdsDumper.setLibraryId(m_library_id);
const auto opdsFeed = opdsDumper.dumpOPDSFeedV2(bookIds, request.get_query());
return ContentResponse::build(
*this,
RESOURCE::catalog_v2_entries_xml,
kainjow::mustache::object{
{"date", now},
{"endpoint_root", root_url + "/catalog/v2"},
{"feed_id", gen_uuid(m_library_id + "/entries?"+request.get_query())},
{"filter", query},
{"books", bookData }
},
opdsFeed,
"application/atom+xml;profile=opds-catalog;kind=acquisition"
);
}

View File

@ -24,6 +24,7 @@
#include "tools/regexTools.h"
#include "tools/stringTools.h"
#include "tools/otherTools.h"
#include "string.h"
#include <mustache.hpp>
@ -38,17 +39,6 @@ namespace
{
// some utilities
std::string render_template(const std::string& template_str, kainjow::mustache::data data)
{
kainjow::mustache::mustache tmpl(template_str);
kainjow::mustache::data urlencode{kainjow::mustache::lambda2{
[](const std::string& str,const kainjow::mustache::renderer& r) { return urlEncode(r(str), true); }}};
data.set("urlencoded", urlencode);
std::stringstream ss;
tmpl.render(data, [&ss](const std::string& str) { ss << str; });
return ss.str();
}
std::string get_mime_type(const zim::Item& item)
{
try {

View File

@ -33,6 +33,8 @@
#include <sstream>
#include <pugixml.hpp>
#include <zim/uuid.h>
static std::map<std::string, std::string> codeisomapping {
{ "aa", "aar" },
@ -358,3 +360,19 @@ std::string kiwix::gen_date_str()
<< std::setw(2) << std::setfill('0') << tm->tm_sec << "Z";
return is.str();
}
std::string kiwix::gen_uuid(const std::string& s)
{
return kiwix::to_string(zim::Uuid::generate(s));
}
std::string kiwix::render_template(const std::string& template_str, kainjow::mustache::data data)
{
kainjow::mustache::mustache tmpl(template_str);
kainjow::mustache::data urlencode{kainjow::mustache::lambda2{
[](const std::string& str,const kainjow::mustache::renderer& r) { return urlEncode(r(str), true); }}};
data.set("urlencoded", urlencode);
std::stringstream ss;
tmpl.render(data, [&ss](const std::string& str) { ss << str; });
return ss.str();
}