mirror of https://github.com/kiwix/libkiwix.git
/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:
parent
dfad1c3815
commit
cdacc0caf1
|
@ -51,11 +51,26 @@ class OPDSDumper
|
||||||
/**
|
/**
|
||||||
* Dump the OPDS feed.
|
* 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.
|
* @return The OPDS feed.
|
||||||
*/
|
*/
|
||||||
std::string dumpOPDSFeed(const std::vector<std::string>& bookIds);
|
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.
|
* Set the id of the opds stream.
|
||||||
*
|
*
|
||||||
|
@ -103,6 +118,7 @@ class OPDSDumper
|
||||||
protected:
|
protected:
|
||||||
kiwix::Library* library;
|
kiwix::Library* library;
|
||||||
std::string id;
|
std::string id;
|
||||||
|
std::string libraryId;
|
||||||
std::string title;
|
std::string title;
|
||||||
std::string date;
|
std::string date;
|
||||||
std::string rootLocation;
|
std::string rootLocation;
|
||||||
|
|
|
@ -24,6 +24,7 @@
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <zim/zim.h>
|
#include <zim/zim.h>
|
||||||
|
#include <mustache.hpp>
|
||||||
|
|
||||||
namespace pugi {
|
namespace pugi {
|
||||||
class xml_node;
|
class xml_node;
|
||||||
|
@ -47,6 +48,9 @@ namespace kiwix
|
||||||
MimeCounterType parseMimetypeCounter(const std::string& counterData);
|
MimeCounterType parseMimetypeCounter(const std::string& counterData);
|
||||||
|
|
||||||
std::string gen_date_str();
|
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
|
#endif
|
||||||
|
|
|
@ -22,6 +22,9 @@
|
||||||
|
|
||||||
#include "tools/otherTools.h"
|
#include "tools/otherTools.h"
|
||||||
|
|
||||||
|
#include "kiwixlib-resources.h"
|
||||||
|
#include <mustache.hpp>
|
||||||
|
|
||||||
namespace kiwix
|
namespace kiwix
|
||||||
{
|
{
|
||||||
/* Constructor */
|
/* Constructor */
|
||||||
|
@ -135,4 +138,45 @@ string OPDSDumper::dumpOPDSFeed(const std::vector<std::string>& bookIds)
|
||||||
return nodeToString(root_node);
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -79,11 +79,6 @@ namespace kiwix {
|
||||||
namespace
|
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)
|
inline std::string normalizeRootUrl(const std::string& rootUrl)
|
||||||
{
|
{
|
||||||
return (rootUrl.empty() || rootUrl[0] == '/')
|
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)
|
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 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 count = request.get_optional_param("count", 10UL);
|
||||||
const size_t start = request.get_optional_param("start", 0UL);
|
const size_t start = request.get_optional_param("start", 0UL);
|
||||||
for ( const auto& bookId : subrange(allMatchingEntries, start, count) ) {
|
const auto bookIds = subrange(mp_library->filter(filter), start, count);
|
||||||
const Book& book = mp_library->getBookById(bookId);
|
OPDSDumper opdsDumper(mp_library);
|
||||||
const MustacheData bookUrl = book.getUrl().empty()
|
opdsDumper.setRootLocation(normalizeRootUrl(m_root));
|
||||||
? MustacheData(false)
|
opdsDumper.setLibraryId(m_library_id);
|
||||||
: MustacheData(book.getUrl());
|
const auto opdsFeed = opdsDumper.dumpOPDSFeedV2(bookIds, request.get_query());
|
||||||
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());
|
|
||||||
|
|
||||||
return ContentResponse::build(
|
return ContentResponse::build(
|
||||||
*this,
|
*this,
|
||||||
RESOURCE::catalog_v2_entries_xml,
|
opdsFeed,
|
||||||
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 }
|
|
||||||
},
|
|
||||||
"application/atom+xml;profile=opds-catalog;kind=acquisition"
|
"application/atom+xml;profile=opds-catalog;kind=acquisition"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,6 +24,7 @@
|
||||||
|
|
||||||
#include "tools/regexTools.h"
|
#include "tools/regexTools.h"
|
||||||
#include "tools/stringTools.h"
|
#include "tools/stringTools.h"
|
||||||
|
#include "tools/otherTools.h"
|
||||||
|
|
||||||
#include "string.h"
|
#include "string.h"
|
||||||
#include <mustache.hpp>
|
#include <mustache.hpp>
|
||||||
|
@ -38,17 +39,6 @@ namespace
|
||||||
{
|
{
|
||||||
// some utilities
|
// 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)
|
std::string get_mime_type(const zim::Item& item)
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -33,6 +33,8 @@
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <pugixml.hpp>
|
#include <pugixml.hpp>
|
||||||
|
|
||||||
|
#include <zim/uuid.h>
|
||||||
|
|
||||||
|
|
||||||
static std::map<std::string, std::string> codeisomapping {
|
static std::map<std::string, std::string> codeisomapping {
|
||||||
{ "aa", "aar" },
|
{ "aa", "aar" },
|
||||||
|
@ -358,3 +360,19 @@ std::string kiwix::gen_date_str()
|
||||||
<< std::setw(2) << std::setfill('0') << tm->tm_sec << "Z";
|
<< std::setw(2) << std::setfill('0') << tm->tm_sec << "Z";
|
||||||
return is.str();
|
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();
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue