Compare commits

..

6 Commits

Author SHA1 Message Date
Nikhil Tanwar
2cd057941e Add test for aliasName filtering
Adds test to check if filtering by alias name works.
2022-09-24 20:16:20 +02:00
Nikhil Tanwar
8a3a0b08c2 fixup! Add catalog filtering using ZIM aliasname 2022-09-24 20:16:20 +02:00
Nikhil Tanwar
956c597e80 fixup! Extract parseQuery() 2022-09-24 20:16:20 +02:00
Nikhil Tanwar
bd38ea97f9 Multivalue support for book query
Adds support for putting multiple `book` query parameter.
2022-09-24 20:16:20 +02:00
Nikhil Tanwar
48a0b3bdc7 Add catalog filtering using ZIM aliasname
Adds mechanism to get a ZIM using its alias name.
To make a search, one needs to visit:
`TLD/?book=aliasNameHere`
2022-09-24 20:16:20 +02:00
Nikhil Tanwar
b84eaad748 Extract parseQuery()
Extracts the duplicate code from publisherQuery() and nameQuery() into a new function parseQuery().
2022-09-24 20:16:20 +02:00
33 changed files with 476 additions and 492 deletions

View File

@@ -9,6 +9,7 @@ jobs:
matrix:
distro:
- ubuntu-jammy
- ubuntu-impish
- ubuntu-focal
- ubuntu-bionic
steps:
@@ -41,6 +42,14 @@ jobs:
args: --no-sign
ppa: ${{ steps.ppa.outputs.ppa }}
- uses: legoktm/gh-action-build-deb@ubuntu-impish
if: matrix.distro == 'ubuntu-impish'
name: Build package for ubuntu-impish
id: build-ubuntu-impish
with:
args: --no-sign
ppa: ${{ steps.ppa.outputs.ppa }}
- uses: legoktm/gh-action-build-deb@ubuntu-focal
if: matrix.distro == 'ubuntu-focal'
name: Build package for ubuntu-focal

38
format_code.sh Executable file
View File

@@ -0,0 +1,38 @@
#!/usr/bin/bash
files=(
"include/library.h"
"include/common/stringTools.h"
"include/common/pathTools.h"
"include/common/otherTools.h"
"include/common/regexTools.h"
"include/common/networkTools.h"
"include/common/archiveTools.h"
"include/manager.h"
"include/reader.h"
"include/kiwix.h"
"include/xapianSearcher.h"
"include/searcher.h"
"src/library.cpp"
"src/android/kiwix.cpp"
"src/android/org/kiwix/kiwixlib/JNIKiwixBool.java"
"src/android/org/kiwix/kiwixlib/JNIKiwix.java"
"src/android/org/kiwix/kiwixlib/JNIKiwixString.java"
"src/android/org/kiwix/kiwixlib/JNIKiwixInt.java"
"src/searcher.cpp"
"src/common/pathTools.cpp"
"src/common/regexTools.cpp"
"src/common/otherTools.cpp"
"src/common/archiveTools.cpp"
"src/common/networkTools.cpp"
"src/common/stringTools.cpp"
"src/xapianSearcher.cpp"
"src/manager.cpp"
"src/reader.cpp"
)
for i in "${files[@]}"
do
echo $i
clang-format -i -style=file $i
done

View File

@@ -54,6 +54,7 @@ enum supportedListMode {
class Filter {
public: // types
using Tags = std::vector<std::string>;
using AliasNames = std::vector<std::string>;
private: // data
uint64_t activeFilters;
@@ -67,6 +68,7 @@ class Filter {
std::string _query;
bool _queryIsPartial;
std::string _name;
AliasNames _aliasNames;
public: // functions
Filter();
@@ -112,6 +114,7 @@ class Filter {
Filter& maxSize(size_t size);
Filter& query(std::string query, bool partial=true);
Filter& name(std::string name);
Filter& aliasNames(const AliasNames& aliasNames);
bool hasQuery() const;
const std::string& getQuery() const { return _query; }
@@ -135,6 +138,8 @@ class Filter {
const Tags& getAcceptTags() const { return _acceptTags; }
const Tags& getRejectTags() const { return _rejectTags; }
const AliasNames& getAliasNames() const { return _aliasNames; }
private: // functions
friend class Library;

View File

@@ -37,10 +37,10 @@ namespace kiwix
class LibraryManipulator
{
public: // functions
explicit LibraryManipulator(std::shared_ptr<Library> library);
explicit LibraryManipulator(Library* library);
virtual ~LibraryManipulator();
Library& getLibrary() const { return *library.get(); }
Library& getLibrary() const { return library; }
bool addBookToLibrary(const Book& book);
void addBookmarkToLibrary(const Bookmark& bookmark);
@@ -52,7 +52,7 @@ class LibraryManipulator
virtual void booksWereRemovedFromLibrary();
private: // data
std::shared_ptr<kiwix::Library> library;
kiwix::Library& library;
};
/**
@@ -65,7 +65,7 @@ class Manager
public: // functions
explicit Manager(LibraryManipulator* manipulator);
explicit Manager(std::shared_ptr<Library> library);
explicit Manager(Library* library);
/**
* Read a `library.xml` and add book in the file to the library.

View File

@@ -50,16 +50,17 @@ class HumanReadableNameMapper : public NameMapper {
std::map<std::string, std::string> m_nameToId;
public:
HumanReadableNameMapper(const kiwix::Library& library, bool withAlias);
HumanReadableNameMapper(kiwix::Library& library, bool withAlias);
virtual ~HumanReadableNameMapper() = default;
virtual std::string getNameForId(const std::string& id) const;
virtual std::string getIdForName(const std::string& name) const;
static std::string removeDateFromBookId(const std::string& bookId);
};
class UpdatableNameMapper : public NameMapper {
typedef std::shared_ptr<NameMapper> NameMapperHandle;
public:
UpdatableNameMapper(std::shared_ptr<Library> library, bool withAlias);
UpdatableNameMapper(Library& library, bool withAlias);
virtual std::string getNameForId(const std::string& id) const;
virtual std::string getIdForName(const std::string& name) const;
@@ -71,7 +72,7 @@ class UpdatableNameMapper : public NameMapper {
private:
mutable std::mutex mutex;
std::shared_ptr<Library> library;
Library& library;
NameMapperHandle nameMapper;
const bool withAlias;
};

View File

@@ -23,11 +23,10 @@
#include <time.h>
#include <sstream>
#include <string>
#include <vector>
#include <pugixml.hpp>
#include "server.h"
#include "library.h"
using namespace std;
@@ -41,7 +40,8 @@ namespace kiwix
class OPDSDumper
{
public:
OPDSDumper(Server::Configuration configuration);
OPDSDumper() = default;
OPDSDumper(Library* library);
~OPDSDumper();
/**
@@ -92,6 +92,13 @@ class OPDSDumper
*/
void setLibraryId(const std::string& id) { this->libraryId = id;}
/**
* Set the root location used when generating url.
*
* @param rootLocation the root location to use.
*/
void setRootLocation(const std::string& rootLocation) { this->rootLocation = rootLocation; }
/**
* Set some informations about the search results.
*
@@ -102,8 +109,9 @@ class OPDSDumper
void setOpenSearchInfo(int totalResult, int startIndex, int count);
protected:
Server::Configuration m_configuration;
kiwix::Library* library;
std::string libraryId;
std::string rootLocation;
int m_totalResults;
int m_startIndex;
int m_count;

View File

@@ -46,7 +46,7 @@ class SearchRenderer
* @param start The start offset used for the srs.
* @param estimatedResultCount The estimatedResultCount of the whole search
*/
SearchRenderer(zim::SearchResultSet srs, std::shared_ptr<NameMapper> mapper,
SearchRenderer(zim::SearchResultSet srs, NameMapper* mapper,
unsigned int start, unsigned int estimatedResultCount);
/**
@@ -58,7 +58,7 @@ class SearchRenderer
* @param start The start offset used for the srs.
* @param estimatedResultCount The estimatedResultCount of the whole search
*/
SearchRenderer(zim::SearchResultSet srs, std::shared_ptr<NameMapper> mapper, std::shared_ptr<Library> library,
SearchRenderer(zim::SearchResultSet srs, NameMapper* mapper, Library* library,
unsigned int start, unsigned int estimatedResultCount);
~SearchRenderer();
@@ -106,8 +106,8 @@ class SearchRenderer
protected:
std::string beautifyInteger(const unsigned int number);
zim::SearchResultSet m_srs;
std::shared_ptr<NameMapper> mp_nameMapper;
std::shared_ptr<Library> mp_library;
NameMapper* mp_nameMapper;
Library* mp_library;
std::string searchBookQuery;
std::string searchPattern;
std::string protocolPrefix;

View File

@@ -29,81 +29,15 @@ namespace kiwix
class NameMapper;
class InternalServer;
class Server {
public:
class Configuration {
public:
explicit Configuration(std::shared_ptr<Library> library, std::shared_ptr<NameMapper> nameMapper=nullptr);
Configuration& setRoot(const std::string& root);
Configuration& setAddress(const std::string& addr) {
m_addr = addr;
return *this;
}
Configuration& setPort(int port) {
m_port = port;
return *this;
}
Configuration& setNbThreads(int threads) {
m_nbThreads = threads;
return *this;
}
Configuration& setMultiZimSearchLimit(unsigned int limit) {
m_multizimSearchLimit = limit;
return *this;
}
Configuration& setIpConnectionLimit(int limit) {
m_ipConnectionLimit = limit;
return *this;
}
Configuration& setVerbose(bool verbose) {
m_verbose = verbose;
return *this;
}
Configuration& setIndexTemplateString(const std::string& indexTemplateString) {
m_indexTemplateString = indexTemplateString;
return *this;
}
Configuration& setTaskbar(bool withTaskbar, bool withLibraryButton) {
m_withTaskbar = withTaskbar;
m_withLibraryButton = withLibraryButton;
return *this;
}
Configuration& setBlockExternalLinks(bool blockExternalLinks) {
m_blockExternalLinks = blockExternalLinks;
return *this;
}
std::shared_ptr<Library> mp_library;
std::shared_ptr<NameMapper> mp_nameMapper;
std::string m_root = "";
std::string m_addr = "";
std::string m_indexTemplateString = "";
int m_port = 80;
int m_nbThreads = 1;
unsigned int m_multizimSearchLimit = 0;
bool m_verbose = false;
bool m_withTaskbar = true;
bool m_withLibraryButton = true;
bool m_blockExternalLinks = false;
int m_ipConnectionLimit = 0;
};
public:
/**
* The default constructor.
*
* @param library The library to serve.
*/
explicit Server(const Configuration& configuration);
Server(Library* library, NameMapper* nameMapper=nullptr);
virtual ~Server();
/**
@@ -116,22 +50,35 @@ namespace kiwix
*/
void stop();
/**
* Tell if the server is running or not.
*/
bool isRunning();
/**
* Get the port of the server
*/
void setRoot(const std::string& root);
void setAddress(const std::string& addr) { m_addr = addr; }
void setPort(int port) { m_port = port; }
void setNbThreads(int threads) { m_nbThreads = threads; }
void setMultiZimSearchLimit(unsigned int limit) { m_multizimSearchLimit = limit; }
void setIpConnectionLimit(int limit) { m_ipConnectionLimit = limit; }
void setVerbose(bool verbose) { m_verbose = verbose; }
void setIndexTemplateString(const std::string& indexTemplateString) { m_indexTemplateString = indexTemplateString; }
void setTaskbar(bool withTaskbar, bool withLibraryButton)
{ m_withTaskbar = withTaskbar; m_withLibraryButton = withLibraryButton; }
void setBlockExternalLinks(bool blockExternalLinks)
{ m_blockExternalLinks = blockExternalLinks; }
int getPort();
/**
* Get the ipAddress of the server
*/
std::string getAddress();
protected:
Library* mp_library;
NameMapper* mp_nameMapper;
std::string m_root = "";
std::string m_addr = "";
std::string m_indexTemplateString = "";
int m_port = 80;
int m_nbThreads = 1;
unsigned int m_multizimSearchLimit = 0;
bool m_verbose = false;
bool m_withTaskbar = true;
bool m_withLibraryButton = true;
bool m_blockExternalLinks = false;
int m_ipConnectionLimit = 0;
std::unique_ptr<InternalServer> mp_server;
};
}

View File

@@ -1,14 +0,0 @@
#!/usr/bin/bash
# Compute 'src' path
SCRIPT_DIR=$(dirname "$0")
REPO_DIR=$(readlink -f "$SCRIPT_DIR"/..)
DIRS="src include"
# Apply formating to all *.cpp and *.h files
cd "$REPO_DIR"
for FILE in $(find $DIRS -name '*.h' -o -name '*.cpp')
do
echo $FILE
clang-format -i -style=file "$FILE"
done

View File

@@ -28,6 +28,7 @@
#include "tools/stringTools.h"
#include "tools/otherTools.h"
#include "tools/concurrent_cache.h"
#include "name_mapper.h"
#include <pugixml.hpp>
#include <algorithm>
@@ -461,6 +462,9 @@ void Library::updateBookDB(const Book& book)
indexer.index_text(normalizeText(book.getPublisher()), 1, "XP");
indexer.index_text(normalizeText(book.getName()), 1, "XN");
indexer.index_text(normalizeText(book.getCategory()), 1, "XC");
const auto bookName = book.getHumanReadableIdFromPath();
const auto aliasName = HumanReadableNameMapper::removeDateFromBookId(bookName);
indexer.index_text(normalizeText(aliasName), 1, "XF");
for ( const auto& tag : split(normalizeText(book.getTags()), ";") ) {
doc.add_boolean_term("XT" + tag);
@@ -505,6 +509,7 @@ Xapian::Query buildXapianQueryFromFilterQuery(const Filter& filter)
queryParser.add_prefix("publisher", "XP");
queryParser.add_prefix("creator", "A");
queryParser.add_prefix("tag", "XT");
queryParser.add_prefix("filename", "XF");
const auto partialQueryFlag = filter.queryIsPartial()
? Xapian::QueryParser::FLAG_PARTIAL
: 0;
@@ -521,6 +526,16 @@ Xapian::Query buildXapianQueryFromFilterQuery(const Filter& filter)
return queryParser.parse_query(normalizeText(filter.getQuery()), flags);
}
Xapian::Query makePhraseQuery(const std::string& query, const std::string& prefix)
{
Xapian::QueryParser queryParser;
queryParser.set_default_op(Xapian::Query::OP_OR);
queryParser.set_stemming_strategy(Xapian::QueryParser::STEM_NONE);
const auto flags = 0;
const auto q = queryParser.parse_query(normalizeText(query), flags, prefix);
return Xapian::Query(Xapian::Query::OP_PHRASE, q.get_terms_begin(), q.get_terms_end(), q.get_length());
}
Xapian::Query nameQuery(const std::string& name)
{
return Xapian::Query("XN" + normalizeText(name));
@@ -531,6 +546,18 @@ Xapian::Query categoryQuery(const std::string& category)
return Xapian::Query("XC" + normalizeText(category));
}
Xapian::Query aliasNamesQuery(const Filter::AliasNames& aliasNames)
{
Xapian::Query q = Xapian::Query(std::string());
std::vector<Xapian::Query> queryVec;
for (const auto& aliasName : aliasNames) {
queryVec.push_back(makePhraseQuery(aliasName, "XF"));
}
Xapian::Query combinedQuery(Xapian::Query::OP_OR, queryVec.begin(), queryVec.end());
q = Xapian::Query(Xapian::Query::OP_FILTER, q, combinedQuery);
return q;
}
Xapian::Query langQuery(const std::string& lang)
{
return Xapian::Query("L" + normalizeText(lang));
@@ -538,22 +565,12 @@ Xapian::Query langQuery(const std::string& lang)
Xapian::Query publisherQuery(const std::string& publisher)
{
Xapian::QueryParser queryParser;
queryParser.set_default_op(Xapian::Query::OP_OR);
queryParser.set_stemming_strategy(Xapian::QueryParser::STEM_NONE);
const auto flags = 0;
const auto q = queryParser.parse_query(normalizeText(publisher), flags, "XP");
return Xapian::Query(Xapian::Query::OP_PHRASE, q.get_terms_begin(), q.get_terms_end(), q.get_length());
return makePhraseQuery(publisher, "XP");
}
Xapian::Query creatorQuery(const std::string& creator)
{
Xapian::QueryParser queryParser;
queryParser.set_default_op(Xapian::Query::OP_OR);
queryParser.set_stemming_strategy(Xapian::QueryParser::STEM_NONE);
const auto flags = 0;
const auto q = queryParser.parse_query(normalizeText(creator), flags, "A");
return Xapian::Query(Xapian::Query::OP_PHRASE, q.get_terms_begin(), q.get_terms_end(), q.get_length());
return makePhraseQuery(creator, "A");
}
Xapian::Query tagsQuery(const Filter::Tags& acceptTags, const Filter::Tags& rejectTags)
@@ -593,6 +610,9 @@ Xapian::Query buildXapianQuery(const Filter& filter)
const auto tq = tagsQuery(filter.getAcceptTags(), filter.getRejectTags());
q = Xapian::Query(Xapian::Query::OP_AND, q, tq);;
}
if ( !filter.getAliasNames().empty() ) {
q = Xapian::Query(Xapian::Query::OP_AND, q, aliasNamesQuery(filter.getAliasNames()));
}
return q;
}
@@ -742,6 +762,7 @@ enum filterTypes {
QUERY = FLAG(12),
NAME = FLAG(13),
CATEGORY = FLAG(14),
ALIASNAMES = FLAG(15),
};
Filter& Filter::local(bool accept)
@@ -844,6 +865,13 @@ Filter& Filter::name(std::string name)
return *this;
}
Filter& Filter::aliasNames(const AliasNames& aliasNames)
{
_aliasNames = aliasNames;
activeFilters |= ALIASNAMES;
return *this;
}
#define ACTIVE(X) (activeFilters & (X))
#define FILTER(TAG, TEST) if (ACTIVE(TAG) && !(TEST)) { return false; }
bool Filter::hasQuery() const

View File

@@ -41,8 +41,8 @@ struct NoDelete
// LibraryManipulator
////////////////////////////////////////////////////////////////////////////////
LibraryManipulator::LibraryManipulator(std::shared_ptr<Library> library)
: library(library)
LibraryManipulator::LibraryManipulator(Library* library)
: library(*library)
{}
LibraryManipulator::~LibraryManipulator()
@@ -50,7 +50,7 @@ LibraryManipulator::~LibraryManipulator()
bool LibraryManipulator::addBookToLibrary(const Book& book)
{
const auto ret = library->addBook(book);
const auto ret = library.addBook(book);
if ( ret ) {
bookWasAddedToLibrary(book);
}
@@ -59,13 +59,13 @@ bool LibraryManipulator::addBookToLibrary(const Book& book)
void LibraryManipulator::addBookmarkToLibrary(const Bookmark& bookmark)
{
library->addBookmark(bookmark);
library.addBookmark(bookmark);
bookmarkWasAddedToLibrary(bookmark);
}
uint32_t LibraryManipulator::removeBooksNotUpdatedSince(Library::Revision rev)
{
const auto n = library->removeBooksNotUpdatedSince(rev);
const auto n = library.removeBooksNotUpdatedSince(rev);
if ( n != 0 ) {
booksWereRemovedFromLibrary();
}
@@ -95,7 +95,7 @@ Manager::Manager(LibraryManipulator* manipulator):
{
}
Manager::Manager(std::shared_ptr<Library> library) :
Manager::Manager(Library* library) :
writableLibraryPath(""),
manipulator(new LibraryManipulator(library))
{

View File

@@ -24,7 +24,7 @@
namespace kiwix {
HumanReadableNameMapper::HumanReadableNameMapper(const kiwix::Library& library, bool withAlias) {
HumanReadableNameMapper::HumanReadableNameMapper(kiwix::Library& library, bool withAlias) {
for (auto& bookId: library.filter(kiwix::Filter().local(true).valid(true))) {
auto& currentBook = library.getBookById(bookId);
auto bookName = currentBook.getHumanReadableIdFromPath();
@@ -34,7 +34,7 @@ HumanReadableNameMapper::HumanReadableNameMapper(const kiwix::Library& library,
if (!withAlias)
continue;
auto aliasName = replaceRegex(bookName, "", "_[[:digit:]]{4}-[[:digit:]]{2}$");
auto aliasName = removeDateFromBookId(bookName);
if (aliasName == bookName) {
continue;
}
@@ -51,6 +51,10 @@ HumanReadableNameMapper::HumanReadableNameMapper(const kiwix::Library& library,
}
}
std::string HumanReadableNameMapper::removeDateFromBookId(const std::string& bookId) {
return replaceRegex(bookId, "", "_[[:digit:]]{4}-[[:digit:]]{2}$");
}
std::string HumanReadableNameMapper::getNameForId(const std::string& id) const {
return m_idToName.at(id);
}
@@ -63,7 +67,7 @@ std::string HumanReadableNameMapper::getIdForName(const std::string& name) const
// UpdatableNameMapper
////////////////////////////////////////////////////////////////////////////////
UpdatableNameMapper::UpdatableNameMapper(std::shared_ptr<Library> lib, bool withAlias)
UpdatableNameMapper::UpdatableNameMapper(Library& lib, bool withAlias)
: library(lib)
, withAlias(withAlias)
{
@@ -72,7 +76,7 @@ UpdatableNameMapper::UpdatableNameMapper(std::shared_ptr<Library> lib, bool with
void UpdatableNameMapper::update()
{
const auto newNameMapper = new HumanReadableNameMapper(*library, withAlias);
const auto newNameMapper = new HumanReadableNameMapper(library, withAlias);
std::lock_guard<std::mutex> lock(mutex);
nameMapper.reset(newNameMapper);
}

View File

@@ -19,8 +19,6 @@
#include "opds_dumper.h"
#include "book.h"
#include "library.h"
#include "name_mapper.h"
#include "libkiwix-resources.h"
#include <mustache.hpp>
@@ -32,8 +30,8 @@ namespace kiwix
{
/* Constructor */
OPDSDumper::OPDSDumper(Server::Configuration configuration)
: m_configuration(configuration)
OPDSDumper::OPDSDumper(Library* library)
: library(library)
{
}
/* Destructor */
@@ -51,8 +49,6 @@ void OPDSDumper::setOpenSearchInfo(int totalResults, int startIndex, int count)
namespace
{
const std::string XML_HEADER(R"(<?xml version="1.0" encoding="UTF-8"?>)");
typedef kainjow::mustache::data MustacheData;
typedef kainjow::mustache::list BooksData;
typedef kainjow::mustache::list IllustrationInfo;
@@ -73,17 +69,16 @@ IllustrationInfo getBookIllustrationInfo(const Book& book)
return illustrations;
}
std::string fullEntryXML(const Server::Configuration& configuration, const Book& book)
kainjow::mustache::object getSingleBookData(const Book& book)
{
const auto bookDate = book.getDate() + "T00:00:00Z";
const kainjow::mustache::object data{
{"root", configuration.m_root},
return kainjow::mustache::object{
{"id", book.getId()},
{"name", book.getName()},
{"title", book.getTitle()},
{"description", book.getDescription()},
{"language", book.getLanguage()},
{"content_id", urlEncode(configuration.mp_nameMapper->getNameForId(book.getId()), true)},
{"content_id", urlEncode(book.getHumanReadableIdFromPath(), true)},
{"updated", bookDate}, // XXX: this should be the entry update datetime
{"book_date", bookDate},
{"category", book.getCategory()},
@@ -97,33 +92,27 @@ std::string fullEntryXML(const Server::Configuration& configuration, const Book&
{"size", to_string(book.getSize())},
{"icons", getBookIllustrationInfo(book)},
};
return render_template(RESOURCE::templates::catalog_v2_entry_xml, data);
}
std::string partialEntryXML(const Server::Configuration& configuration, const Book& book)
std::string getSingleBookEntryXML(const Book& book, bool withXMLHeader, const std::string& rootLocation, const std::string& endpointRoot, bool partial)
{
const auto bookDate = book.getDate() + "T00:00:00Z";
const kainjow::mustache::object data{
{"root", configuration.m_root},
{"endpoint_root", configuration.m_root + "/catalog/v2"},
{"id", book.getId()},
{"title", book.getTitle()},
{"updated", bookDate}, // XXX: this should be the entry update datetime
};
const auto xmlTemplate = RESOURCE::templates::catalog_v2_partial_entry_xml;
return render_template(xmlTemplate, data);
auto data = getSingleBookData(book);
data["with_xml_header"] = MustacheData(withXMLHeader);
data["dump_partial_entries"] = MustacheData(partial);
data["endpoint_root"] = endpointRoot;
data["root"] = rootLocation;
return render_template(RESOURCE::templates::catalog_v2_entry_xml, data);
}
BooksData getBooksData(const Server::Configuration& configuration, const std::vector<std::string>& bookIds, bool partial)
BooksData getBooksData(const Library* library, const std::vector<std::string>& bookIds, const std::string& rootLocation, const std::string& endpointRoot, bool partial)
{
BooksData booksData;
for ( const auto& bookId : bookIds ) {
try {
const Book book = configuration.mp_library->getBookByIdThreadSafe(bookId);
const auto entryXML = partial
? partialEntryXML(configuration, book)
: fullEntryXML(configuration, book);
booksData.push_back(kainjow::mustache::object{ {"entry", entryXML} });
const Book book = library->getBookByIdThreadSafe(bookId);
booksData.push_back(kainjow::mustache::object{
{"entry", getSingleBookEntryXML(book, false, rootLocation, endpointRoot, partial)}
});
} catch ( const std::out_of_range& ) {
// the book was removed from the library since its id was obtained
// ignore it
@@ -190,10 +179,10 @@ std::string getLanguageSelfName(const std::string& lang) {
string OPDSDumper::dumpOPDSFeed(const std::vector<std::string>& bookIds, const std::string& query) const
{
const auto booksData = getBooksData(m_configuration, bookIds, false);
const auto booksData = getBooksData(library, bookIds, rootLocation, "", false);
const kainjow::mustache::object template_data{
{"date", gen_date_str()},
{"root", m_configuration.m_root},
{"root", rootLocation},
{"feed_id", gen_uuid(libraryId + "/catalog/search?"+query)},
{"filter", onlyAsNonEmptyMustacheValue(query)},
{"totalResults", to_string(m_totalResults)},
@@ -207,8 +196,8 @@ string OPDSDumper::dumpOPDSFeed(const std::vector<std::string>& bookIds, const s
string OPDSDumper::dumpOPDSFeedV2(const std::vector<std::string>& bookIds, const std::string& query, bool partial) const
{
const auto endpointRoot = m_configuration.m_root + "/catalog/v2";
const auto booksData = getBooksData(m_configuration, bookIds, partial);
const auto endpointRoot = rootLocation + "/catalog/v2";
const auto booksData = getBooksData(library, bookIds, rootLocation, endpointRoot, partial);
const char* const endpoint = partial ? "/partial_entries" : "/entries";
const kainjow::mustache::object template_data{
@@ -229,17 +218,14 @@ string OPDSDumper::dumpOPDSFeedV2(const std::vector<std::string>& bookIds, const
std::string OPDSDumper::dumpOPDSCompleteEntry(const std::string& bookId) const
{
const auto book = m_configuration.mp_library->getBookById(bookId);
return XML_HEADER
+ "\n"
+ fullEntryXML(m_configuration, book);
return getSingleBookEntryXML(library->getBookById(bookId), true, rootLocation, "", false);
}
std::string OPDSDumper::categoriesOPDSFeed() const
{
const auto now = gen_date_str();
kainjow::mustache::list categoryData;
for ( const auto& category : m_configuration.mp_library->getBooksCategories() ) {
for ( const auto& category : library->getBooksCategories() ) {
const auto urlencodedCategoryName = urlEncode(category);
categoryData.push_back(kainjow::mustache::object{
{"name", category},
@@ -253,7 +239,7 @@ std::string OPDSDumper::categoriesOPDSFeed() const
RESOURCE::templates::catalog_v2_categories_xml,
kainjow::mustache::object{
{"date", now},
{"endpoint_root", m_configuration.m_root + "/catalog/v2"},
{"endpoint_root", rootLocation + "/catalog/v2"},
{"feed_id", gen_uuid(libraryId + "/categories")},
{"categories", categoryData }
}
@@ -265,7 +251,7 @@ std::string OPDSDumper::languagesOPDSFeed() const
const auto now = gen_date_str();
kainjow::mustache::list languageData;
std::call_once(fillLanguagesFlag, fillLanguagesMap);
for ( const auto& langAndBookCount : m_configuration.mp_library->getBooksLanguagesWithCounts() ) {
for ( const auto& langAndBookCount : library->getBooksLanguagesWithCounts() ) {
const std::string languageCode = langAndBookCount.first;
const int bookCount = langAndBookCount.second;
const auto languageSelfName = getLanguageSelfName(languageCode);
@@ -282,7 +268,7 @@ std::string OPDSDumper::languagesOPDSFeed() const
RESOURCE::templates::catalog_v2_languages_xml,
kainjow::mustache::object{
{"date", now},
{"endpoint_root", m_configuration.m_root + "/catalog/v2"},
{"endpoint_root", rootLocation + "/catalog/v2"},
{"feed_id", gen_uuid(libraryId + "/languages")},
{"languages", languageData }
}

View File

@@ -36,12 +36,12 @@ namespace kiwix
{
/* Constructor */
SearchRenderer::SearchRenderer(zim::SearchResultSet srs, std::shared_ptr<NameMapper> mapper,
SearchRenderer::SearchRenderer(zim::SearchResultSet srs, NameMapper* mapper,
unsigned int start, unsigned int estimatedResultCount)
: SearchRenderer(srs, mapper, nullptr, start, estimatedResultCount)
{}
SearchRenderer::SearchRenderer(zim::SearchResultSet srs, std::shared_ptr<NameMapper> mapper, std::shared_ptr<Library> library,
SearchRenderer::SearchRenderer(zim::SearchResultSet srs, NameMapper* mapper, Library* library,
unsigned int start, unsigned int estimatedResultCount)
: m_srs(srs),
mp_nameMapper(mapper),
@@ -166,7 +166,7 @@ kainjow::mustache::data buildPagination(
std::string SearchRenderer::renderTemplate(const std::string& tmpl_str)
{
const std::string absPathPrefix = protocolPrefix;
const std::string absPathPrefix = protocolPrefix + "content/";
// Build the results list
kainjow::mustache::data items{kainjow::mustache::data::type::list};
for (auto it = m_srs.begin(); it != m_srs.end(); it++) {
@@ -206,7 +206,7 @@ std::string SearchRenderer::renderTemplate(const std::string& tmpl_str)
kainjow::mustache::data allData;
allData.set("searchProtocolPrefix", searchProtocolPrefix);
allData.set("protocolPrefix", protocolPrefix);
allData.set("results", results);
allData.set("pagination", pagination);
allData.set("query", query);

View File

@@ -26,38 +26,52 @@
#include <zim/item.h>
#include "server/internalServer.h"
#include "tools/otherTools.h"
namespace kiwix {
Server::Configuration::Configuration(std::shared_ptr<Library> library, std::shared_ptr<NameMapper> nameMapper) :
Server::Server(Library* library, NameMapper* nameMapper) :
mp_library(library),
mp_nameMapper(nameMapper ? nameMapper : std::make_shared<HumanReadableNameMapper>(*library, true))
{}
Server::Configuration& Server::Configuration::setRoot(const std::string& root)
{
m_root = normalizeRootUrl(root);
return *this;
}
Server::Server(const Server::Configuration& configuration) :
mp_server(new InternalServer(configuration))
mp_nameMapper(nameMapper),
mp_server(nullptr)
{
}
Server::~Server() = default;
bool Server::start() {
mp_server.reset(new InternalServer(
mp_library,
mp_nameMapper,
m_addr,
m_port,
m_root,
m_nbThreads,
m_multizimSearchLimit,
m_verbose,
m_withTaskbar,
m_withLibraryButton,
m_blockExternalLinks,
m_indexTemplateString,
m_ipConnectionLimit));
return mp_server->start();
}
void Server::stop() {
mp_server->stop();
if (mp_server) {
mp_server->stop();
mp_server.reset(nullptr);
}
}
bool Server::isRunning() {
return mp_server->isRunning();
void Server::setRoot(const std::string& root)
{
m_root = root;
if (m_root[0] != '/') {
m_root = "/" + m_root;
}
if (m_root.back() == '/') {
m_root.erase(m_root.size() - 1);
}
}
int Server::getPort()

View File

@@ -85,6 +85,16 @@ namespace kiwix {
namespace
{
inline std::string normalizeRootUrl(std::string rootUrl)
{
while ( !rootUrl.empty() && rootUrl.back() == '/' )
rootUrl.pop_back();
while ( !rootUrl.empty() && rootUrl.front() == '/' )
rootUrl = rootUrl.substr(1);
return rootUrl.empty() ? rootUrl : "/" + rootUrl;
}
Filter get_search_filter(const RequestContext& request, const std::string& prefix="")
{
auto filter = kiwix::Filter().valid(true).local(true);
@@ -109,6 +119,9 @@ Filter get_search_filter(const RequestContext& request, const std::string& prefi
try {
filter.rejectTags(kiwix::split(request.get_argument(prefix+"notag"), ";"));
} catch (...) {}
try {
filter.aliasNames(request.get_arguments(prefix + "book"));
} catch (...) {}
return filter;
}
@@ -323,6 +336,8 @@ zim::Query SearchInfo::getZimQuery(bool verbose) const {
return query;
}
static IdNameMapper defaultNameMapper;
static MHD_Result staticHandlerCallback(void* cls,
struct MHD_Connection* connection,
const char* url,
@@ -354,10 +369,33 @@ public:
};
InternalServer::InternalServer(const Server::Configuration& configuration) :
Server::Configuration(configuration),
m_indexTemplateString(configuration.m_indexTemplateString.empty() ? RESOURCE::templates::index_html : configuration.m_indexTemplateString),
InternalServer::InternalServer(Library* library,
NameMapper* nameMapper,
std::string addr,
int port,
std::string root,
int nbThreads,
unsigned int multizimSearchLimit,
bool verbose,
bool withTaskbar,
bool withLibraryButton,
bool blockExternalLinks,
std::string indexTemplateString,
int ipConnectionLimit) :
m_addr(addr),
m_port(port),
m_root(normalizeRootUrl(root)),
m_nbThreads(nbThreads),
m_multizimSearchLimit(multizimSearchLimit),
m_verbose(verbose),
m_withTaskbar(withTaskbar),
m_withLibraryButton(withLibraryButton),
m_blockExternalLinks(blockExternalLinks),
m_indexTemplateString(indexTemplateString.empty() ? RESOURCE::templates::index_html : indexTemplateString),
m_ipConnectionLimit(ipConnectionLimit),
mp_daemon(nullptr),
mp_library(library),
mp_nameMapper(nameMapper ? nameMapper : &defaultNameMapper),
searchCache(getEnvVar<int>("KIWIX_SEARCH_CACHE_SIZE", DEFAULT_CACHE_SIZE)),
suggestionSearcherCache(getEnvVar<int>("KIWIX_SUGGESTION_SEARCHER_CACHE_SIZE", std::max((unsigned int) (mp_library->getBookCount(true, true)*0.1), 1U))),
m_customizedResources(new CustomizedResources)
@@ -371,7 +409,7 @@ bool InternalServer::start() {
#else
int flags = MHD_USE_POLL_INTERNALLY;
#endif
if (m_verbose)
if (m_verbose.load())
flags |= MHD_USE_DEBUG;
struct sockaddr_in sockAddr;
@@ -415,13 +453,8 @@ bool InternalServer::start() {
void InternalServer::stop()
{
MHD_stop_daemon(mp_daemon);
mp_daemon = nullptr;
}
bool InternalServer::isRunning() const
{
return mp_daemon != nullptr;
}
static MHD_Result staticHandlerCallback(void* cls,
struct MHD_Connection* connection,
const char* url,
@@ -451,14 +484,14 @@ MHD_Result InternalServer::handlerCallback(struct MHD_Connection* connection,
void** cont_cls)
{
auto start_time = std::chrono::steady_clock::now();
if (m_verbose) {
if (m_verbose.load() ) {
printf("======================\n");
printf("Requesting : \n");
printf("full_url : %s\n", url);
}
RequestContext request(connection, m_root, url, method, version);
if (m_verbose) {
if (m_verbose.load() ) {
request.print_debug_info();
}
/* Unexpected method */
@@ -474,7 +507,7 @@ MHD_Result InternalServer::handlerCallback(struct MHD_Connection* connection,
if (response->getReturnCode() == MHD_HTTP_INTERNAL_SERVER_ERROR) {
printf("========== INTERNAL ERROR !! ============\n");
if (!m_verbose) {
if (!m_verbose.load()) {
printf("Requesting : \n");
printf("full_url : %s\n", url);
request.print_debug_info();
@@ -487,7 +520,7 @@ MHD_Result InternalServer::handlerCallback(struct MHD_Connection* connection,
auto ret = response->send(request, connection);
auto end_time = std::chrono::steady_clock::now();
auto time_span = std::chrono::duration_cast<std::chrono::duration<double>>(end_time - start_time);
if (m_verbose) {
if (m_verbose.load()) {
printf("Request time : %fs\n", time_span.count());
printf("----------------------\n");
}
@@ -603,24 +636,9 @@ std::unique_ptr<Response> InternalServer::build_homepage(const RequestContext& r
* Archive and Zim handlers begin
**/
class InternalServer::LockableSuggestionSearcher : public zim::SuggestionSearcher
{
public:
explicit LockableSuggestionSearcher(const zim::Archive& archive)
: zim::SuggestionSearcher(archive)
{}
std::unique_lock<std::mutex> getLock() {
return std::unique_lock<std::mutex>(m_mutex);
}
virtual ~LockableSuggestionSearcher() = default;
private:
std::mutex m_mutex;
};
std::unique_ptr<Response> InternalServer::handle_suggest(const RequestContext& request)
{
if (m_verbose) {
if (m_verbose.load()) {
printf("** running handle_suggest\n");
}
@@ -651,7 +669,7 @@ std::unique_ptr<Response> InternalServer::handle_suggest(const RequestContext& r
count = 10;
}
if (m_verbose) {
if (m_verbose.load()) {
printf("Searching suggestions for: \"%s\"\n", queryString.c_str());
}
@@ -661,9 +679,8 @@ std::unique_ptr<Response> InternalServer::handle_suggest(const RequestContext& r
/* Get the suggestions */
auto searcher = suggestionSearcherCache.getOrPut(bookId,
[=](){ return make_shared<LockableSuggestionSearcher>(*archive); }
[=](){ return make_shared<zim::SuggestionSearcher>(*archive); }
);
const auto lock(searcher->getLock());
auto search = searcher->suggest(queryString);
auto srs = search.getResults(start, count);
@@ -704,7 +721,7 @@ std::unique_ptr<Response> InternalServer::handle_suggest(const RequestContext& r
std::unique_ptr<Response> InternalServer::handle_viewer_settings(const RequestContext& request)
{
if (m_verbose) {
if (m_verbose.load()) {
printf("** running handle_viewer_settings\n");
}
@@ -718,7 +735,7 @@ std::unique_ptr<Response> InternalServer::handle_viewer_settings(const RequestCo
std::unique_ptr<Response> InternalServer::handle_skin(const RequestContext& request)
{
if (m_verbose) {
if (m_verbose.load()) {
printf("** running handle_skin\n");
}
@@ -741,7 +758,7 @@ std::unique_ptr<Response> InternalServer::handle_skin(const RequestContext& requ
std::unique_ptr<Response> InternalServer::handle_search(const RequestContext& request)
{
if (m_verbose) {
if (m_verbose.load()) {
printf("** running handle_search\n");
}
@@ -770,7 +787,7 @@ std::unique_ptr<Response> InternalServer::handle_search(const RequestContext& re
try {
search = searchCache.getOrPut(searchInfo,
[=](){
return make_shared<zim::Search>(searcher->search(searchInfo.getZimQuery(m_verbose)));
return make_shared<zim::Search>(searcher->search(searchInfo.getZimQuery(m_verbose.load())));
}
);
} catch(std::runtime_error& e) {
@@ -816,7 +833,7 @@ std::unique_ptr<Response> InternalServer::handle_search(const RequestContext& re
search->getEstimatedMatches());
renderer.setSearchPattern(searchInfo.pattern);
renderer.setSearchBookQuery(searchInfo.bookFilterQuery);
renderer.setProtocolPrefix(m_root + "/content/");
renderer.setProtocolPrefix(m_root + "/");
renderer.setSearchProtocolPrefix(m_root + "/search");
renderer.setPageLength(pageLength);
if (request.get_requested_format() == "xml") {
@@ -842,7 +859,7 @@ std::unique_ptr<Response> InternalServer::handle_search(const RequestContext& re
std::unique_ptr<Response> InternalServer::handle_random(const RequestContext& request)
{
if (m_verbose) {
if (m_verbose.load()) {
printf("** running handle_random\n");
}
@@ -894,7 +911,7 @@ std::unique_ptr<Response> InternalServer::handle_captured_external(const Request
std::unique_ptr<Response> InternalServer::handle_catch(const RequestContext& request)
{
if (m_verbose) {
if (m_verbose.load()) {
printf("** running handle_catch\n");
}
@@ -908,7 +925,7 @@ std::unique_ptr<Response> InternalServer::handle_catch(const RequestContext& req
std::unique_ptr<Response> InternalServer::handle_catalog(const RequestContext& request)
{
if (m_verbose) {
if (m_verbose.load()) {
printf("** running handle_catalog");
}
@@ -937,7 +954,8 @@ std::unique_ptr<Response> InternalServer::handle_catalog(const RequestContext& r
}
zim::Uuid uuid;
kiwix::OPDSDumper opdsDumper(*this);
kiwix::OPDSDumper opdsDumper(mp_library);
opdsDumper.setRootLocation(m_root);
opdsDumper.setLibraryId(m_library_id);
std::vector<std::string> bookIdsToDump;
if (url == "root.xml") {
@@ -999,7 +1017,7 @@ std::unique_ptr<Response> InternalServer::handle_content(const RequestContext& r
{
const std::string url = request.get_url();
const std::string pattern = url.substr((url.find_last_of('/'))+1);
if (m_verbose) {
if (m_verbose.load()) {
printf("** running handle_content\n");
}
@@ -1028,26 +1046,21 @@ std::unique_ptr<Response> InternalServer::handle_content(const RequestContext& r
try {
auto entry = getEntryFromPath(*archive, urlStr);
if (entry.isRedirect() || urlStr != entry.getPath()) {
// In the condition above, the second case (an entry with a different
// URL was returned) can occur in the following situations:
// 1. urlStr is empty or equal to "/" and the ZIM file doesn't contain
// such an entry, in which case the main entry is returned instead.
// 2. The ZIM file uses old namespace scheme, and the resource at urlStr
// is not present but can be found under one of the 'A', 'I', 'J' or
// '-' namespaces, in which case that resource is returned instead.
if (entry.isRedirect() || urlStr.empty()) {
// If urlStr is empty, we want to mainPage.
// We must do a redirection to the real page.
return build_redirect(bookName, getFinalItem(*archive, entry));
}
auto response = ItemResponse::build(*this, request, entry.getItem());
if (m_verbose) {
if (m_verbose.load()) {
printf("Found %s\n", entry.getPath().c_str());
printf("mimeType: %s\n", entry.getItem(true).getMimetype().c_str());
}
return response;
} catch(zim::EntryNotFound& e) {
if (m_verbose)
if (m_verbose.load())
printf("Failed to find %s\n", urlStr.c_str());
std::string searchURL = m_root + "/search?content=" + bookName + "&pattern=" + kiwix::urlEncode(pattern, true);
@@ -1060,7 +1073,7 @@ std::unique_ptr<Response> InternalServer::handle_content(const RequestContext& r
std::unique_ptr<Response> InternalServer::handle_raw(const RequestContext& request)
{
if (m_verbose) {
if (m_verbose.load()) {
printf("** running handle_raw\n");
}
@@ -1110,7 +1123,7 @@ std::unique_ptr<Response> InternalServer::handle_raw(const RequestContext& reque
return ItemResponse::build(*this, request, entry.getItem());
}
} catch (zim::EntryNotFound& e ) {
if (m_verbose) {
if (m_verbose.load()) {
printf("Failed to find %s\n", itemPath.c_str());
}
return HTTP404Response(*this, request)
@@ -1126,13 +1139,13 @@ bool InternalServer::isLocallyCustomizedResource(const std::string& url) const
std::unique_ptr<Response> InternalServer::handle_locally_customized_resource(const RequestContext& request)
{
if (m_verbose) {
if (m_verbose.load()) {
printf("** running handle_locally_customized_resource\n");
}
const CustomizedResourceData& crd = m_customizedResources->at(request.get_url());
if (m_verbose) {
if (m_verbose.load()) {
std::cout << "Reading " << crd.resourceFilePath << std::endl;
}
const auto resourceData = getFileContent(crd.resourceFilePath);

View File

@@ -26,7 +26,7 @@ extern "C" {
}
#include "library.h"
#include "server.h"
#include "name_mapper.h"
#include <zim/search.h>
#include <zim/suggestion.h>
@@ -88,11 +88,26 @@ class SearchInfo {
typedef kainjow::mustache::data MustacheData;
typedef ConcurrentCache<SearchInfo, std::shared_ptr<zim::Search>> SearchCache;
typedef ConcurrentCache<std::string, std::shared_ptr<zim::SuggestionSearcher>> SuggestionSearcherCache;
class OPDSDumper;
class InternalServer : Server::Configuration {
class InternalServer {
public:
InternalServer(const Server::Configuration& configuration);
InternalServer(Library* library,
NameMapper* nameMapper,
std::string addr,
int port,
std::string root,
int nbThreads,
unsigned int multizimSearchLimit,
bool verbose,
bool withTaskbar,
bool withLibraryButton,
bool blockExternalLinks,
std::string indexTemplateString,
int ipConnectionLimit);
virtual ~InternalServer();
MHD_Result handlerCallback(struct MHD_Connection* connection,
@@ -104,7 +119,6 @@ class InternalServer : Server::Configuration {
void** cont_cls);
bool start();
void stop();
bool isRunning() const;
std::string getAddress() { return m_addr; }
int getPort() { return m_port; }
@@ -143,16 +157,23 @@ class InternalServer : Server::Configuration {
bool isLocallyCustomizedResource(const std::string& url) const;
private: // types
class LockableSuggestionSearcher;
typedef ConcurrentCache<SearchInfo, std::shared_ptr<zim::Search>> SearchCache;
typedef ConcurrentCache<std::string, std::shared_ptr<LockableSuggestionSearcher>> SuggestionSearcherCache;
private: // data
std::string m_addr;
int m_port;
std::string m_root;
int m_nbThreads;
unsigned int m_multizimSearchLimit;
std::atomic_bool m_verbose;
bool m_withTaskbar;
bool m_withLibraryButton;
bool m_blockExternalLinks;
std::string m_indexTemplateString;
int m_ipConnectionLimit;
struct MHD_Daemon* mp_daemon;
Library* mp_library;
NameMapper* mp_nameMapper;
SearchCache searchCache;
SuggestionSearcherCache suggestionSearcherCache;

View File

@@ -35,7 +35,7 @@ namespace kiwix {
std::unique_ptr<Response> InternalServer::handle_catalog_v2(const RequestContext& request)
{
if (m_verbose) {
if (m_verbose.load()) {
printf("** running handle_catalog_v2");
}
@@ -95,7 +95,8 @@ std::unique_ptr<Response> InternalServer::handle_catalog_v2_root(const RequestCo
std::unique_ptr<Response> InternalServer::handle_catalog_v2_entries(const RequestContext& request, bool partial)
{
OPDSDumper opdsDumper(*this);
OPDSDumper opdsDumper(mp_library);
opdsDumper.setRootLocation(m_root);
opdsDumper.setLibraryId(m_library_id);
const auto bookIds = search_catalog(request, opdsDumper);
const auto opdsFeed = opdsDumper.dumpOPDSFeedV2(bookIds, request.get_query(), partial);
@@ -115,7 +116,8 @@ std::unique_ptr<Response> InternalServer::handle_catalog_v2_complete_entry(const
+ urlNotFoundMsg;
}
OPDSDumper opdsDumper(*this);
OPDSDumper opdsDumper(mp_library);
opdsDumper.setRootLocation(m_root);
opdsDumper.setLibraryId(m_library_id);
const auto opdsFeed = opdsDumper.dumpOPDSCompleteEntry(entryId);
return ContentResponse::build(
@@ -127,7 +129,8 @@ std::unique_ptr<Response> InternalServer::handle_catalog_v2_complete_entry(const
std::unique_ptr<Response> InternalServer::handle_catalog_v2_categories(const RequestContext& request)
{
OPDSDumper opdsDumper(*this);
OPDSDumper opdsDumper(mp_library);
opdsDumper.setRootLocation(m_root);
opdsDumper.setLibraryId(m_library_id);
return ContentResponse::build(
*this,
@@ -138,7 +141,8 @@ std::unique_ptr<Response> InternalServer::handle_catalog_v2_categories(const Req
std::unique_ptr<Response> InternalServer::handle_catalog_v2_languages(const RequestContext& request)
{
OPDSDumper opdsDumper(*this);
OPDSDumper opdsDumper(mp_library);
opdsDumper.setRootLocation(m_root);
opdsDumper.setLibraryId(m_library_id);
return ContentResponse::build(
*this,

View File

@@ -114,7 +114,7 @@ Response::Response(bool verbose)
std::unique_ptr<Response> Response::build(const InternalServer& server)
{
return std::unique_ptr<Response>(new Response(server.m_verbose));
return std::unique_ptr<Response>(new Response(server.m_verbose.load()));
}
std::unique_ptr<Response> Response::build_304(const InternalServer& server, const ETag& etag)
@@ -390,7 +390,7 @@ std::unique_ptr<ContentResponse> ContentResponse::build(
{
return std::unique_ptr<ContentResponse>(new ContentResponse(
server.m_root,
server.m_verbose,
server.m_verbose.load(),
content,
mimetype));
}
@@ -435,7 +435,7 @@ std::unique_ptr<Response> ItemResponse::build(const InternalServer& server, cons
}
return std::unique_ptr<Response>(new ItemResponse(
server.m_verbose,
server.m_verbose.load(),
item,
mimetype,
byteRange));

View File

@@ -370,16 +370,6 @@ std::string kiwix::gen_uuid(const std::string& s)
return kiwix::to_string(zim::Uuid::generate(s));
}
std::string kiwix::normalizeRootUrl(std::string rootUrl)
{
while ( !rootUrl.empty() && rootUrl.back() == '/' )
rootUrl.pop_back();
while ( !rootUrl.empty() && rootUrl.front() == '/' )
rootUrl = rootUrl.substr(1);
return rootUrl.empty() ? rootUrl : "/" + rootUrl;
}
kainjow::mustache::data kiwix::onlyAsNonEmptyMustacheValue(const std::string& s)
{
return s.empty()

View File

@@ -51,8 +51,6 @@ namespace kiwix
std::string gen_date_str();
std::string gen_uuid(const std::string& s);
std::string normalizeRootUrl(std::string rootUrl);
// if s is empty then returns kainjow::mustache::data(false)
// otherwise kainjow::mustache::data(value)
kainjow::mustache::data onlyAsNonEmptyMustacheValue(const std::string& s);

View File

@@ -27,7 +27,6 @@ templates/catalog_entries.xml
templates/catalog_v2_root.xml
templates/catalog_v2_entries.xml
templates/catalog_v2_entry.xml
templates/catalog_v2_partial_entry.xml
templates/catalog_v2_categories.xml
templates/catalog_v2_languages.xml
templates/url_of_search_results_css

View File

@@ -152,27 +152,6 @@ function updateSearchBoxForBookChange() {
}
}
let previousScrollTop = Infinity;
function updateToolbarVisibilityState() {
const iframeDoc = contentIframe.contentDocument;
const st = iframeDoc.documentElement.scrollTop || iframeDoc.body.scrollTop;
if ( Math.abs(previousScrollTop - st) <= 5 )
return;
const kiwixToolBar = document.querySelector('#kiwixtoolbar');
if (st > previousScrollTop) {
kiwixToolBar.style.position = 'fixed';
kiwixToolBar.style.top = '-100%';
} else {
kiwixToolBar.style.position = 'static';
kiwixToolBar.style.top = '0';
}
previousScrollTop = st;
}
function handle_visual_viewport_change() {
contentIframe.height = window.visualViewport.height - contentIframe.offsetTop - 4;
}
@@ -186,7 +165,6 @@ function handle_location_hash_change() {
contentIframe.contentWindow.location.replace(iframeContentUrl);
}
updateSearchBoxForLocationChange();
previousScrollTop = Infinity;
}
function handle_content_url_change() {
@@ -298,7 +276,39 @@ function htmlDecode(input) {
}
function setupAutoHidingOfTheToolbar() {
setInterval(updateToolbarVisibilityState, 250);
let lastScrollTop = 0;
const delta = 5;
let didScroll = false;
const kiwixToolBar = document.querySelector('#kiwixtoolbar');
contentIframe.contentWindow.addEventListener('scroll', () => {
didScroll = true;
});
setInterval(function() {
if (didScroll) {
hasScrolled();
didScroll = false;
}
}, 250);
function hasScrolled() {
const iframeDoc = contentIframe.contentDocument;
const st = iframeDoc.documentElement.scrollTop || iframeDoc.body.scrollTop;
if (Math.abs(lastScrollTop - st) <= delta)
return;
if (st > lastScrollTop) {
kiwixToolBar.style.position = 'fixed';
kiwixToolBar.style.top = '-100%';
} else {
kiwixToolBar.style.position = 'static';
kiwixToolBar.style.top = '0';
}
lastScrollTop = st;
}
}
function setupSuggestions() {
@@ -374,10 +384,7 @@ function setupSuggestions() {
}
function setupViewer() {
// Defer the call of handle_visual_viewport_change() until after the
// presence or absence of the taskbar as determined by this function
// has been settled.
setTimeout(handle_visual_viewport_change, 0);
setInterval(handle_visual_viewport_change, 0);
const kiwixToolBarWrapper = document.getElementById('kiwixtoolbarwrapper');
if ( ! viewerSettings.toolbarEnabled ) {

View File

@@ -1,8 +1,13 @@
<entry>
{{#with_xml_header}}<?xml version="1.0" encoding="UTF-8"?>
{{/with_xml_header}} <entry>
<id>urn:uuid:{{id}}</id>
<title>{{title}}</title>
<updated>{{updated}}</updated>
<summary>{{description}}</summary>
{{#dump_partial_entries}}
<link rel="alternate"
href="{{endpoint_root}}/entry/{{{id}}}"
type="application/atom+xml;type=entry;profile=opds-catalog"/>
{{/dump_partial_entries}}{{^dump_partial_entries}} <summary>{{description}}</summary>
<language>{{language}}</language>
<name>{{name}}</name>
<flavour>{{flavour}}</flavour>
@@ -24,4 +29,5 @@
{{#url}}
<link rel="http://opds-spec.org/acquisition/open-access" type="application/x-zim" href="{{{url}}}" length="{{{size}}}" />
{{/url}}
{{/dump_partial_entries}}
</entry>

View File

@@ -1,8 +0,0 @@
<entry>
<id>urn:uuid:{{id}}</id>
<title>{{title}}</title>
<updated>{{updated}}</updated>
<link rel="alternate"
href="{{endpoint_root}}/entry/{{{id}}}"
type="application/atom+xml;type=entry;profile=opds-catalog"/>
</entry>

View File

@@ -9,7 +9,7 @@
<opensearch:totalResults>{{results.count}}</opensearch:totalResults>
<opensearch:startIndex>{{results.start}}</opensearch:startIndex>
<opensearch:itemsPerPage>{{pagination.itemsPerPage}}</opensearch:itemsPerPage>
<atom:link rel="search" type="application/opensearchdescription+xml" href="{{searchProtocolPrefix}}/searchdescription.xml"/>
<atom:link rel="search" type="application/opensearchdescription+xml" href="{{protocolPrefix}}search/searchdescription.xml"/>
<opensearch:Query role="request"
searchTerms="{{query.pattern}}"{{#query.lang}}
language="{{query.lang}}"{{/query.lang}}

View File

@@ -20,7 +20,6 @@
#include "gtest/gtest.h"
#include <string>
#include "testing_tools.h"
const char * sampleOpdsStream = R"(
<feed xmlns="http://www.w3.org/2005/Atom"
@@ -238,7 +237,7 @@ namespace
TEST(LibraryOpdsImportTest, allInOne)
{
kiwix::Library lib;
kiwix::Manager manager{NotOwned<kiwix::Library>(lib)};
kiwix::Manager manager(&lib);
manager.readOpds(sampleOpdsStream, "library-opds-import.unittests.dev");
EXPECT_EQ(10U, lib.getBookCount(true, true));
@@ -298,11 +297,8 @@ class LibraryTest : public ::testing::Test {
typedef kiwix::Library::BookIdCollection BookIdCollection;
typedef std::vector<std::string> TitleCollection;
explicit LibraryTest()
{}
void SetUp() override {
kiwix::Manager manager{NotOwned<kiwix::Library>(lib)};
kiwix::Manager manager(&lib);
manager.readOpds(sampleOpdsStream, "foo.urlHost");
manager.readXml(sampleLibraryXML, false, "./test/library.xml", true);
}
@@ -504,6 +500,24 @@ TEST_F(LibraryTest, filterByTags)
);
}
TEST_F(LibraryTest, filterByAliasNames)
{
// filtering for one book
EXPECT_FILTER_RESULTS(kiwix::Filter().aliasNames({"zimfile"}),
"Ray Charles"
);
// filerting for more than one book
EXPECT_FILTER_RESULTS(kiwix::Filter().aliasNames({"zimfile", "example"}),
"An example ZIM archive",
"Ray Charles"
);
// filtering by alias name requires full text match
EXPECT_FILTER_RESULTS(kiwix::Filter().aliasNames({"wrong_name"}),
/* no results */
);
}
TEST_F(LibraryTest, filterByQuery)
{

View File

@@ -18,13 +18,8 @@ protected:
const int PORT = 8002;
protected:
void resetServer(ZimFileServer::Options options) {
zfs1_.reset();
zfs1_.reset(new ZimFileServer(PORT, options, "./test/library.xml"));
}
void SetUp() override {
zfs1_.reset(new ZimFileServer(PORT, ZimFileServer::DEFAULT_OPTIONS, "./test/library.xml"));
zfs1_.reset(new ZimFileServer(PORT, "./test/library.xml"));
}
void TearDown() override {
@@ -75,20 +70,20 @@ std::string maskVariableOPDSFeedData(std::string s)
" type=\"application/opensearchdescription+xml\"" \
" href=\"/ROOT/catalog/searchdescription.xml\" />\n"
#define CATALOG_ENTRY(UUID, TITLE, SUMMARY, LANG, NAME, CATEGORY, TAGS, EXTRA_LINK, CONTENT_NAME, FILE_NAME, LENGTH) \
#define CHARLES_RAY_CATALOG_ENTRY \
" <entry>\n" \
" <id>urn:uuid:" UUID "</id>\n" \
" <title>" TITLE "</title>\n" \
" <id>urn:uuid:charlesray</id>\n" \
" <title>Charles, Ray</title>\n" \
" <updated>YYYY-MM-DDThh:mm:ssZ</updated>\n" \
" <summary>" SUMMARY "</summary>\n" \
" <language>" LANG "</language>\n" \
" <name>" NAME "</name>\n" \
" <summary>Wikipedia articles about Ray Charles</summary>\n" \
" <language>fra</language>\n" \
" <name>wikipedia_fr_ray_charles</name>\n" \
" <flavour></flavour>\n" \
" <category>" CATEGORY "</category>\n" \
" <tags>" TAGS "</tags>\n" \
" <category>jazz</category>\n" \
" <tags>unittest;wikipedia;_category:jazz;_pictures:no;_videos:no;_details:no;_ftindex:yes</tags>\n" \
" <articleCount>284</articleCount>\n" \
" <mediaCount>2</mediaCount>\n" \
" " EXTRA_LINK "<link type=\"text/html\" href=\"/ROOT/content/" CONTENT_NAME "\" />\n" \
" <link type=\"text/html\" href=\"/ROOT/content/zimfile%26other\" />\n" \
" <author>\n" \
" <name>Wikipedia</name>\n" \
" </author>\n" \
@@ -96,57 +91,59 @@ std::string maskVariableOPDSFeedData(std::string s)
" <name>Kiwix</name>\n" \
" </publisher>\n" \
" <dc:issued>2020-03-31T00:00:00Z</dc:issued>\n" \
" <link rel=\"http://opds-spec.org/acquisition/open-access\" type=\"application/x-zim\" href=\"https://github.com/kiwix/libkiwix/raw/master/test/data/" FILE_NAME ".zim\" length=\"" LENGTH "\" />\n" \
" <link rel=\"http://opds-spec.org/acquisition/open-access\" type=\"application/x-zim\" href=\"https://github.com/kiwix/libkiwix/raw/master/test/data/zimfile%26other.zim\" length=\"569344\" />\n" \
" </entry>\n"
#define RAY_CHARLES_CATALOG_ENTRY \
" <entry>\n" \
" <id>urn:uuid:raycharles</id>\n" \
" <title>Ray Charles</title>\n" \
" <updated>YYYY-MM-DDThh:mm:ssZ</updated>\n" \
" <summary>Wikipedia articles about Ray Charles</summary>\n" \
" <language>eng</language>\n" \
" <name>wikipedia_en_ray_charles</name>\n" \
" <flavour></flavour>\n" \
" <category>wikipedia</category>\n" \
" <tags>public_tag_without_a_value;_private_tag_without_a_value;wikipedia;_category:wikipedia;_pictures:no;_videos:no;_details:no;_ftindex:yes</tags>\n" \
" <articleCount>284</articleCount>\n" \
" <mediaCount>2</mediaCount>\n" \
" <link rel=\"http://opds-spec.org/image/thumbnail\"\n" \
" href=\"/ROOT/catalog/v2/illustration/raycharles/?size=48\"\n" \
" type=\"image/png;width=48;height=48;scale=1\"/>\n" \
" <link type=\"text/html\" href=\"/ROOT/content/zimfile\" />\n" \
" <author>\n" \
" <name>Wikipedia</name>\n" \
" </author>\n" \
" <publisher>\n" \
" <name>Kiwix</name>\n" \
" </publisher>\n" \
" <dc:issued>2020-03-31T00:00:00Z</dc:issued>\n" \
" <link rel=\"http://opds-spec.org/acquisition/open-access\" type=\"application/x-zim\" href=\"https://github.com/kiwix/libkiwix/raw/master/test/data/zimfile.zim\" length=\"569344\" />\n" \
" </entry>\n"
#define _CHARLES_RAY_CATALOG_ENTRY(CONTENT_NAME) CATALOG_ENTRY( \
"charlesray", \
"Charles, Ray", \
"Wikipedia articles about Ray Charles", \
"fra", \
"wikipedia_fr_ray_charles",\
"jazz",\
"unittest;wikipedia;_category:jazz;_pictures:no;_videos:no;_details:no;_ftindex:yes",\
"", \
CONTENT_NAME, \
"zimfile%26other", \
"569344" \
)
#define CHARLES_RAY_CATALOG_ENTRY _CHARLES_RAY_CATALOG_ENTRY("zimfile%26other")
#define _RAY_CHARLES_CATALOG_ENTRY(CONTENT_NAME) CATALOG_ENTRY(\
"raycharles",\
"Ray Charles",\
"Wikipedia articles about Ray Charles",\
"eng",\
"wikipedia_en_ray_charles",\
"wikipedia",\
"public_tag_without_a_value;_private_tag_without_a_value;wikipedia;_category:wikipedia;_pictures:no;_videos:no;_details:no;_ftindex:yes",\
"<link rel=\"http://opds-spec.org/image/thumbnail\"\n" \
" href=\"/ROOT/catalog/v2/illustration/raycharles/?size=48\"\n" \
" type=\"image/png;width=48;height=48;scale=1\"/>\n ", \
CONTENT_NAME, \
"zimfile", \
"569344"\
)
#define RAY_CHARLES_CATALOG_ENTRY _RAY_CHARLES_CATALOG_ENTRY("zimfile")
#define UNCATEGORIZED_RAY_CHARLES_CATALOG_ENTRY CATALOG_ENTRY(\
"raycharles_uncategorized",\
"Ray (uncategorized) Charles",\
"No category is assigned to this library entry.",\
"rus",\
"wikipedia_ru_ray_charles",\
"",\
"public_tag_with_a_value:value_of_a_public_tag;_private_tag_with_a_value:value_of_a_private_tag;wikipedia;_pictures:no;_videos:no;_details:no",\
"",\
"zimfile", \
"zimfile", \
"125952"\
)
#define UNCATEGORIZED_RAY_CHARLES_CATALOG_ENTRY \
" <entry>\n" \
" <id>urn:uuid:raycharles_uncategorized</id>\n" \
" <title>Ray (uncategorized) Charles</title>\n" \
" <updated>YYYY-MM-DDThh:mm:ssZ</updated>\n" \
" <summary>No category is assigned to this library entry.</summary>\n" \
" <language>rus</language>\n" \
" <name>wikipedia_ru_ray_charles</name>\n" \
" <flavour></flavour>\n" \
" <category></category>\n" \
" <tags>public_tag_with_a_value:value_of_a_public_tag;_private_tag_with_a_value:value_of_a_private_tag;wikipedia;_pictures:no;_videos:no;_details:no</tags>\n" \
" <articleCount>284</articleCount>\n" \
" <mediaCount>2</mediaCount>\n" \
" <link type=\"text/html\" href=\"/ROOT/content/zimfile\" />\n" \
" <author>\n" \
" <name>Wikipedia</name>\n" \
" </author>\n" \
" <publisher>\n" \
" <name>Kiwix</name>\n" \
" </publisher>\n" \
" <dc:issued>2020-03-31T00:00:00Z</dc:issued>\n" \
" <link rel=\"http://opds-spec.org/acquisition/open-access\" type=\"application/x-zim\" href=\"https://github.com/kiwix/libkiwix/raw/master/test/data/zimfile.zim\" length=\"125952\" />\n" \
" </entry>\n"
TEST_F(LibraryServerTest, catalog_root_xml)
{
@@ -783,42 +780,4 @@ TEST_F(LibraryServerTest, catalog_search_excludes_hidden_tags)
#undef EXPECT_ZERO_RESULTS
}
#define NO_MAPPER_CHARLES_RAY_CATALOG_ENTRY _CHARLES_RAY_CATALOG_ENTRY("charlesray")
#define NO_MAPPER_RAY_CHARLES_CATALOG_ENTRY _RAY_CHARLES_CATALOG_ENTRY("raycharles")
TEST_F(LibraryServerTest, no_name_mapper_returned_catalog_use_uuid_in_link)
{
resetServer(ZimFileServer::NO_NAME_MAPPER);
const auto r = zfs1_->GET("/ROOT/catalog/search?tag=_category:jazz");
EXPECT_EQ(r->status, 200);
EXPECT_EQ(maskVariableOPDSFeedData(r->body),
OPDS_FEED_TAG
" <id>12345678-90ab-cdef-1234-567890abcdef</id>\n"
" <title>Filtered zims (tag=_category:jazz)</title>\n"
" <updated>YYYY-MM-DDThh:mm:ssZ</updated>\n"
" <totalResults>1</totalResults>\n"
" <startIndex>0</startIndex>\n"
" <itemsPerPage>1</itemsPerPage>\n"
CATALOG_LINK_TAGS
NO_MAPPER_CHARLES_RAY_CATALOG_ENTRY
"</feed>\n"
);
}
TEST_F(LibraryServerTest, no_name_mapper_catalog_v2_individual_entry_access)
{
resetServer(ZimFileServer::NO_NAME_MAPPER);
const auto r = zfs1_->GET("/ROOT/catalog/v2/entry/raycharles");
EXPECT_EQ(r->status, 200);
EXPECT_EQ(maskVariableOPDSFeedData(r->body),
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
NO_MAPPER_RAY_CHARLES_CATALOG_ENTRY
);
const auto r1 = zfs1_->GET("/ROOT/catalog/v2/entry/non-existent-entry");
EXPECT_EQ(r1->status, 404);
}
#undef EXPECT_SEARCH_RESULTS

View File

@@ -6,12 +6,10 @@
#include <iostream>
#include <fstream>
#include "testing_tools.h"
TEST(ManagerTest, addBookFromPathAndGetIdTest)
{
kiwix::Library lib;
kiwix::Manager manager = kiwix::Manager(NotOwned<kiwix::Library>(lib));
kiwix::Manager manager = kiwix::Manager(&lib);
auto bookId = manager.addBookFromPathAndGetId("./test/example.zim");
ASSERT_NE(bookId, "");
@@ -51,7 +49,7 @@ const char sampleLibraryXML[] = R"(
TEST(ManagerTest, readXml)
{
kiwix::Library lib;
kiwix::Manager manager = kiwix::Manager(NotOwned<kiwix::Library>(lib));
kiwix::Manager manager = kiwix::Manager(&lib);
EXPECT_EQ(true, manager.readXml(sampleLibraryXML, true, "/data/lib.xml", true));
kiwix::Book book = lib.getBookById("0d0bcd57-d3f6-cb22-44cc-a723ccb4e1b2");
@@ -73,7 +71,7 @@ TEST(ManagerTest, readXml)
TEST(Manager, reload)
{
kiwix::Library lib;
kiwix::Manager manager = kiwix::Manager(NotOwned<kiwix::Library>(lib));
kiwix::Manager manager(&lib);
manager.reload({ "./test/library.xml" });
EXPECT_EQ(lib.getBooksIds(), (kiwix::Library::BookIdCollection{

View File

@@ -4,12 +4,9 @@
#include "../include/manager.h"
#include "gtest/gtest.h"
#include "testing_tools.h"
namespace
{
const char libraryXML[] = R"(
<library version="1.0">
<book id="01" path="/data/zero_one.zim"> </book>
@@ -21,13 +18,9 @@ const char libraryXML[] = R"(
)";
class NameMapperTest : public ::testing::Test {
public:
explicit NameMapperTest()
{}
protected:
void SetUp() override {
kiwix::Manager manager{NotOwned<kiwix::Library>(lib)};
kiwix::Manager manager(&lib);
manager.readXml(libraryXML, false, "./library.xml", true);
for ( const std::string& id : lib.getBooksIds() ) {
kiwix::Book bookCopy = lib.getBookById(id);
@@ -115,7 +108,7 @@ TEST_F(NameMapperTest, HumanReadableNameMapperWithAliases)
TEST_F(NameMapperTest, UpdatableNameMapperWithoutAliases)
{
CapturedStderr stderror;
kiwix::UpdatableNameMapper nm(NotOwned<kiwix::Library>(lib), false);
kiwix::UpdatableNameMapper nm(lib, false);
EXPECT_EQ("", std::string(stderror));
checkUnaliasedEntriesInNameMapper(nm);
@@ -131,7 +124,7 @@ TEST_F(NameMapperTest, UpdatableNameMapperWithoutAliases)
TEST_F(NameMapperTest, UpdatableNameMapperWithAliases)
{
CapturedStderr stderror;
kiwix::UpdatableNameMapper nm(NotOwned<kiwix::Library>(lib), true);
kiwix::UpdatableNameMapper nm(lib, true);
EXPECT_EQ(
"Path collision: /data/zero_four_2021-10.zim and"
" /data/zero_four_2021-11.zim can't share the same URL path 'zero_four'."

View File

@@ -131,13 +131,6 @@ TEST_F(ServerTest, 200)
EXPECT_EQ(200, zfs1_->GET(res.url)->status) << "res.url: " << res.url;
}
TEST_F(ServerTest, 200_IdNameMapper)
{
resetServer(ZimFileServer::NO_NAME_MAPPER);
EXPECT_EQ(200, zfs1_->GET("/ROOT/content/6f1d19d0-633f-087b-fb55-7ac324ff9baf/A/index")->status);
EXPECT_EQ(404, zfs1_->GET("/ROOT/content/zimfile/A/index")->status);
}
TEST_F(ServerTest, CompressibleContentIsCompressedIfAcceptable)
{
for ( const Resource& res : resources200Compressible ) {
@@ -206,7 +199,7 @@ R"EXPECTEDRESULT( <img src="../skin/download.png?
/* url */ "/ROOT/viewer",
R"EXPECTEDRESULT( <link type="text/css" href="./skin/taskbar.css?cacheid=216d6b5d" rel="Stylesheet" />
<link type="text/css" href="./skin/css/autoComplete.css?cacheid=08951e06" rel="Stylesheet" />
<script type="text/javascript" src="./skin/viewer.js?cacheid=51e745c2" defer></script>
<script type="text/javascript" src="./skin/viewer.js?cacheid=9a336712" defer></script>
<script type="text/javascript" src="./skin/autoComplete.min.js?cacheid=1191aaaf"></script>
const blankPageUrl = `${root}/skin/blank.html`;
<label for="kiwix_button_show_toggle"><img src="./skin/caret.png?cacheid=22b942b4" alt=""></label>

View File

@@ -6,8 +6,6 @@
#include <zim/entry.h>
#include <zim/item.h>
#include "testing_tools.h"
// Output generated via mustache templates sometimes contains end-of-line
// whitespace. This complicates representing the expected output of a unit-test
// as C++ raw strings in editors that are configured to delete EOL whitespace.
@@ -63,7 +61,6 @@ public: // types
WITH_TASKBAR = 1 << 1,
WITH_LIBRARY_BUTTON = 1 << 2,
BLOCK_EXTERNAL_LINKS = 1 << 3,
NO_NAME_MAPPER = 1 << 4,
WITH_TASKBAR_AND_LIBRARY_BUTTON = WITH_TASKBAR | WITH_LIBRARY_BUTTON,
@@ -71,7 +68,7 @@ public: // types
};
public: // functions
ZimFileServer(int serverPort, Options options, std::string libraryFilePath);
ZimFileServer(int serverPort, std::string libraryFilePath);
ZimFileServer(int serverPort,
Options options,
const FilePathCollection& zimpaths,
@@ -94,15 +91,14 @@ private:
private: // data
kiwix::Library library;
kiwix::Manager manager;
std::shared_ptr<kiwix::NameMapper> nameMapper;
std::unique_ptr<kiwix::HumanReadableNameMapper> nameMapper;
std::unique_ptr<kiwix::Server> server;
std::unique_ptr<httplib::Client> client;
const Options options = DEFAULT_OPTIONS;
};
ZimFileServer::ZimFileServer(int serverPort, Options _options, std::string libraryFilePath)
: manager(NotOwned<kiwix::Library>(library)),
options(_options)
ZimFileServer::ZimFileServer(int serverPort, std::string libraryFilePath)
: manager(&this->library)
{
if ( kiwix::isRelativePath(libraryFilePath) )
libraryFilePath = kiwix::computeAbsolutePath(kiwix::getCurrentDirectory(), libraryFilePath);
@@ -114,8 +110,8 @@ ZimFileServer::ZimFileServer(int serverPort,
Options _options,
const FilePathCollection& zimpaths,
std::string indexTemplateString)
: manager(NotOwned<kiwix::Library>(library)),
options(_options)
: manager(&this->library)
, options(_options)
{
for ( const auto& zimpath : zimpaths ) {
if (!manager.addBookFromPath(zimpath, zimpath, "", false))
@@ -127,25 +123,19 @@ ZimFileServer::ZimFileServer(int serverPort,
void ZimFileServer::run(int serverPort, std::string indexTemplateString)
{
const std::string address = "127.0.0.1";
if (options & NO_NAME_MAPPER) {
nameMapper.reset(new kiwix::IdNameMapper());
} else {
nameMapper.reset(new kiwix::HumanReadableNameMapper(library, false));
}
kiwix::Server::Configuration configuration(NotOwned<kiwix::Library>(library), nameMapper);
configuration.setRoot("ROOT")
.setAddress(address)
.setPort(serverPort)
.setNbThreads(2)
.setVerbose(false)
.setTaskbar(options & WITH_TASKBAR, options & WITH_LIBRARY_BUTTON)
.setBlockExternalLinks(options & BLOCK_EXTERNAL_LINKS)
.setMultiZimSearchLimit(3);
nameMapper.reset(new kiwix::HumanReadableNameMapper(library, false));
server.reset(new kiwix::Server(&library, nameMapper.get()));
server->setRoot("ROOT");
server->setAddress(address);
server->setPort(serverPort);
server->setNbThreads(2);
server->setVerbose(false);
server->setTaskbar(options & WITH_TASKBAR, options & WITH_LIBRARY_BUTTON);
server->setBlockExternalLinks(options & BLOCK_EXTERNAL_LINKS);
server->setMultiZimSearchLimit(3);
if (!indexTemplateString.empty()) {
configuration.setIndexTemplateString(indexTemplateString);
server->setIndexTemplateString(indexTemplateString);
}
server.reset(new kiwix::Server(configuration));
if ( !server->start() )
throw std::runtime_error("ZimFileServer failed to start");

View File

@@ -1,19 +0,0 @@
#ifndef TESTING_TOOLS
#define TESTING_TOOLS
#include <memory>
struct NoDelete {
template<class T> void operator()(T*) {}
};
template<class T>
class NotOwned : public std::shared_ptr<T> {
public:
NotOwned(T& object) :
std::shared_ptr<T>(&object, NoDelete()) {};
};
#endif // TESTING_TOOLS