diff --git a/include/name_mapper.h b/include/name_mapper.h index 28706350..374c9d5d 100644 --- a/include/name_mapper.h +++ b/include/name_mapper.h @@ -50,7 +50,7 @@ class HumanReadableNameMapper : public NameMapper { std::map m_nameToId; public: - HumanReadableNameMapper(kiwix::Library& library, bool withAlias); + HumanReadableNameMapper(const 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; diff --git a/include/server.h b/include/server.h index 2825c4fc..9e050215 100644 --- a/include/server.h +++ b/include/server.h @@ -63,6 +63,7 @@ namespace kiwix { m_withTaskbar = withTaskbar; m_withLibraryButton = withLibraryButton; } void setBlockExternalLinks(bool blockExternalLinks) { m_blockExternalLinks = blockExternalLinks; } + void setCatalogOnlyMode(bool enable) { m_catalogOnlyMode = enable; } void setIpMode(IpMode mode) { m_ipMode = mode; } int getPort() const; IpAddress getAddress() const; @@ -83,6 +84,7 @@ namespace kiwix bool m_blockExternalLinks = false; IpMode m_ipMode = IpMode::AUTO; int m_ipConnectionLimit = 0; + bool m_catalogOnlyMode = false; std::unique_ptr mp_server; }; } diff --git a/src/name_mapper.cpp b/src/name_mapper.cpp index 3b3f789d..1fce5178 100644 --- a/src/name_mapper.cpp +++ b/src/name_mapper.cpp @@ -24,8 +24,8 @@ namespace kiwix { -HumanReadableNameMapper::HumanReadableNameMapper(kiwix::Library& library, bool withAlias) { - for (auto& bookId: library.filter(kiwix::Filter().local(true).valid(true))) { +HumanReadableNameMapper::HumanReadableNameMapper(const kiwix::Library& library, bool withAlias) { + for (auto& bookId: library.filter(kiwix::Filter())) { auto& currentBook = library.getBookById(bookId); auto bookName = currentBook.getHumanReadableIdFromPath(); m_idToName[bookId] = bookName; diff --git a/src/server.cpp b/src/server.cpp index 065cefd1..9dc9c41b 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -53,7 +53,8 @@ bool Server::start() { m_blockExternalLinks, m_ipMode, m_indexTemplateString, - m_ipConnectionLimit)); + m_ipConnectionLimit, + m_catalogOnlyMode)); return mp_server->start(); } diff --git a/src/server/internalServer.cpp b/src/server/internalServer.cpp index ab82273f..784bbe9b 100644 --- a/src/server/internalServer.cpp +++ b/src/server/internalServer.cpp @@ -125,9 +125,12 @@ std::string getSearchComponent(const RequestContext& request) return query.empty() ? query : "?" + query; } -Filter get_search_filter(const RequestContext& request, const std::string& prefix="") +Filter get_search_filter(const RequestContext& request, const std::string& prefix="", bool catalogOnlyMode = false) { - auto filter = kiwix::Filter().valid(true).local(true); + auto filter = kiwix::Filter(); + if ( !catalogOnlyMode ) { + filter.valid(true).local(true); + } try { filter.query(request.get_argument(prefix+"q")); } catch (const std::out_of_range&) {} @@ -432,7 +435,8 @@ InternalServer::InternalServer(LibraryPtr library, bool blockExternalLinks, IpMode ipMode, std::string indexTemplateString, - int ipConnectionLimit) : + int ipConnectionLimit, + bool catalogOnlyMode) : m_addr(addr), m_port(port), m_root(normalizeRootUrl(root)), @@ -451,7 +455,8 @@ InternalServer::InternalServer(LibraryPtr library, mp_nameMapper(nameMapper ? nameMapper : std::shared_ptr(&defaultNameMapper, NoDelete())), searchCache(getEnvVar("KIWIX_SEARCH_CACHE_SIZE", DEFAULT_CACHE_SIZE)), suggestionSearcherCache(getEnvVar("KIWIX_SUGGESTION_SEARCHER_CACHE_SIZE", std::max((unsigned int) (mp_library->getBookCount(true, true)*0.1), 1U))), - m_customizedResources(new CustomizedResources) + m_customizedResources(new CustomizedResources), + m_catalogOnlyMode(catalogOnlyMode) { m_root = urlEncode(m_root); } @@ -1103,7 +1108,7 @@ std::vector InternalServer::search_catalog(const RequestContext& request, kiwix::OPDSDumper& opdsDumper) { - const auto filter = get_search_filter(request); + const auto filter = get_search_filter(request, "", m_catalogOnlyMode); std::vector bookIdsToDump = mp_library->filter(filter); const auto totalResults = bookIdsToDump.size(); const long count = request.get_optional_param("count", 10L); diff --git a/src/server/internalServer.h b/src/server/internalServer.h index b0831fef..81f6e48d 100644 --- a/src/server/internalServer.h +++ b/src/server/internalServer.h @@ -106,7 +106,8 @@ class InternalServer { bool blockExternalLinks, IpMode ipMode, std::string indexTemplateString, - int ipConnectionLimit); + int ipConnectionLimit, + bool catalogOnlyMode); virtual ~InternalServer(); MHD_Result handlerCallback(struct MHD_Connection* connection, @@ -192,6 +193,8 @@ class InternalServer { class CustomizedResources; std::unique_ptr m_customizedResources; + + const bool m_catalogOnlyMode; }; } diff --git a/test/library_server.cpp b/test/library_server.cpp index a1dde0ad..4c532724 100644 --- a/test/library_server.cpp +++ b/test/library_server.cpp @@ -150,6 +150,30 @@ std::string maskVariableOPDSFeedData(std::string s) "125952"\ ) +#define INACCESSIBLEZIMFILE_CATALOG_ENTRY \ +" \n" \ +" urn:uuid:inaccessiblezim\n" \ +" Catalog of all catalogs\n" \ +" YYYY-MM-DDThh:mm:ssZ\n" \ +" Testing that running kiwix-serve without access to ZIM files doesn't lead to a catastrophe\n" \ +" cat\n" \ +" catalog_of_all_catalogs\n" \ +" \n" \ +" cats\n" \ +" unittest;_category:cats\n" \ +" 12107\n" \ +" 8\n" \ +" \n" \ +" \n" \ +" Catherine of Catalonia\n" \ +" \n" \ +" \n" \ +" Caterpillar\n" \ +" \n" \ +" 2025-09-04T00:00:00Z\n" \ +" \n" \ +" \n" + TEST_F(LibraryServerTest, catalog_root_xml) { const auto r = zfs1_->GET("/ROOT%23%3F/catalog/root.xml"); @@ -697,6 +721,24 @@ TEST_F(LibraryServerTest, catalog_v2_entries) ); } +TEST_F(LibraryServerTest, catalog_v2_entries_catalog_only_mode) +{ + resetServer(ZimFileServer::CATALOG_ONLY_MODE); + const auto r = zfs1_->GET("/ROOT%23%3F/catalog/v2/entries"); + EXPECT_EQ(r->status, 200); + EXPECT_EQ(maskVariableOPDSFeedData(r->body), + CATALOG_V2_ENTRIES_PREAMBLE("") + " All Entries\n" + " YYYY-MM-DDThh:mm:ssZ\n" + "\n" + CHARLES_RAY_CATALOG_ENTRY + INACCESSIBLEZIMFILE_CATALOG_ENTRY + RAY_CHARLES_CATALOG_ENTRY + UNCATEGORIZED_RAY_CHARLES_CATALOG_ENTRY + "\n" + ); +} + TEST_F(LibraryServerTest, catalog_v2_entries_filtered_by_range) { { diff --git a/test/server_testing_tools.h b/test/server_testing_tools.h index c7adbc56..74b840c9 100644 --- a/test/server_testing_tools.h +++ b/test/server_testing_tools.h @@ -62,6 +62,7 @@ public: // types WITH_LIBRARY_BUTTON = 1 << 2, BLOCK_EXTERNAL_LINKS = 1 << 3, NO_NAME_MAPPER = 1 << 4, + CATALOG_ONLY_MODE = 1 << 5, WITH_TASKBAR_AND_LIBRARY_BUTTON = WITH_TASKBAR | WITH_LIBRARY_BUTTON, @@ -149,6 +150,7 @@ void ZimFileServer::run(int serverPort, std::string indexTemplateString) server->setTaskbar(cfg.options & WITH_TASKBAR, cfg.options & WITH_LIBRARY_BUTTON); server->setBlockExternalLinks(cfg.options & BLOCK_EXTERNAL_LINKS); server->setMultiZimSearchLimit(3); + server->setCatalogOnlyMode(cfg.options & CATALOG_ONLY_MODE); if (!indexTemplateString.empty()) { server->setIndexTemplateString(indexTemplateString); }