diff --git a/include/server.h b/include/server.h index 52f01454..5b439c30 100644 --- a/include/server.h +++ b/include/server.h @@ -56,7 +56,9 @@ namespace kiwix void setNbThreads(int threads) { m_nbThreads = threads; } void setVerbose(bool verbose) { m_verbose = verbose; } void setTaskbar(bool withTaskbar, bool withLibraryButton) - { m_withTaskbar = withTaskbar; m_withLibraryButton = withLibraryButton; } + { setTaskbar(withTaskbar, withLibraryButton, m_blockExternalLinks); } + void setTaskbar(bool withTaskbar, bool withLibraryButton, bool blockExternalLinks) + { m_withTaskbar = withTaskbar; m_withLibraryButton = withLibraryButton; m_blockExternalLinks = blockExternalLinks; } protected: Library* mp_library; @@ -68,6 +70,7 @@ namespace kiwix bool m_verbose = false; bool m_withTaskbar = true; bool m_withLibraryButton = true; + bool m_blockExternalLinks = false; std::unique_ptr mp_server; }; } diff --git a/src/server.cpp b/src/server.cpp index 4af15b92..50a30748 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -95,7 +95,8 @@ class InternalServer { int nbThreads, bool verbose, bool withTaskbar, - bool withLibraryButton); + bool withLibraryButton, + bool blockExternalLinks); virtual ~InternalServer() = default; int handlerCallback(struct MHD_Connection* connection, @@ -119,6 +120,7 @@ class InternalServer { Response handle_search(const RequestContext& request); Response handle_suggest(const RequestContext& request); Response handle_random(const RequestContext& request); + Response handle_captured_external(const RequestContext& request); Response handle_content(const RequestContext& request); kainjow::mustache::data get_default_data(); @@ -131,6 +133,7 @@ class InternalServer { std::atomic_bool m_verbose; bool m_withTaskbar; bool m_withLibraryButton; + bool m_blockExternalLinks; struct MHD_Daemon* mp_daemon; Library* mp_library; @@ -157,7 +160,8 @@ bool Server::start() { m_nbThreads, m_verbose, m_withTaskbar, - m_withLibraryButton)); + m_withLibraryButton, + m_blockExternalLinks)); return mp_server->start(); } @@ -186,7 +190,8 @@ InternalServer::InternalServer(Library* library, int nbThreads, bool verbose, bool withTaskbar, - bool withLibraryButton) : + bool withLibraryButton, + bool blockExternalLinks) : m_addr(addr), m_port(port), m_root(root), @@ -194,6 +199,7 @@ InternalServer::InternalServer(Library* library, m_verbose(verbose), m_withTaskbar(withTaskbar), m_withLibraryButton(withLibraryButton), + m_blockExternalLinks(blockExternalLinks), mp_daemon(nullptr), mp_library(library), mp_nameMapper(nameMapper ? nameMapper : &defaultNameMapper) @@ -340,6 +346,9 @@ Response InternalServer::handle_request(const RequestContext& request) if (request.get_url() == "/random") return handle_random(request); + if (request.get_url() == "/external") + return handle_captured_external(request); + return handle_content(request); } catch (std::exception& e) { fprintf(stderr, "===== Unhandled error : %s\n", e.what()); @@ -359,7 +368,7 @@ kainjow::mustache::data InternalServer::get_default_data() Response InternalServer::get_default_response() { - return Response(m_root, m_verbose.load(), m_withTaskbar, m_withLibraryButton); + return Response(m_root, m_verbose.load(), m_withTaskbar, m_withLibraryButton, m_blockExternalLinks); } @@ -383,7 +392,7 @@ Response InternalServer::build_500(const std::string& msg) { kainjow::mustache::data data; data.set("error", msg); - Response response(m_root, true, false, false); + Response response(m_root, true, false, false, false); response.set_template(RESOURCE::templates::_500_html, data); response.set_mimeType("text/html"); response.set_code(MHD_HTTP_INTERNAL_SERVER_ERROR); @@ -711,6 +720,26 @@ Response InternalServer::handle_random(const RequestContext& request) } } +Response InternalServer::handle_captured_external(const RequestContext& request) +{ + std::string source = ""; + try { + source = kiwix::urlDecode(request.get_argument("source")); + } catch (const std::out_of_range& e) {} + + if (source.empty()) + return build_404(request, ""); + + auto data = get_default_data(); + data.set("source", source); + auto response = get_default_response(); + response.set_template(RESOURCE::templates::captured_external_html, data); + response.set_mimeType("text/html; charset=utf-8"); + response.set_compress(true); + response.set_taskbar("", "", false); + return response; +} + Response InternalServer::handle_catalog(const RequestContext& request) { if (m_verbose.load()) { diff --git a/src/server/response.cpp b/src/server/response.cpp index c55fd63b..4344de7e 100644 --- a/src/server/response.cpp +++ b/src/server/response.cpp @@ -17,7 +17,7 @@ namespace kiwix { -Response::Response(const std::string& root, bool verbose, bool withTaskbar, bool withLibraryButton) +Response::Response(const std::string& root, bool verbose, bool withTaskbar, bool withLibraryButton, bool blockExternalLinks) : m_verbose(verbose), m_root(root), m_content(""), @@ -25,6 +25,7 @@ Response::Response(const std::string& root, bool verbose, bool withTaskbar, bool m_returnCode(MHD_HTTP_OK), m_withTaskbar(withTaskbar), m_withLibraryButton(withLibraryButton), + m_blockExternalLinks(blockExternalLinks), m_useCache(false), m_addTaskbar(false), m_bookName(""), @@ -124,6 +125,14 @@ void Response::introduce_taskbar() m_content, "]*>", taskbar_part); + + if ( m_blockExternalLinks ) { + const std::string capture_external_part = getResource("templates/block_external.js"); + m_content = appendToFirstOccurence( + m_content, + "block external links\n", + capture_external_part); + } } @@ -239,11 +248,12 @@ void Response::set_entry(const Entry& entry) { m_mode = ResponseMode::ENTRY; } -void Response::set_taskbar(const std::string& bookName, const std::string& bookTitle) +void Response::set_taskbar(const std::string& bookName, const std::string& bookTitle, bool blockExternalLinks) { m_addTaskbar = true; m_bookName = bookName; m_bookTitle = bookTitle; + m_blockExternalLinks = blockExternalLinks; } diff --git a/src/server/response.h b/src/server/response.h index df372c25..0340b013 100644 --- a/src/server/response.h +++ b/src/server/response.h @@ -42,7 +42,7 @@ class RequestContext; class Response { public: - Response(const std::string& root, bool verbose, bool withTaskbar, bool withLibraryButton); + Response(const std::string& root, bool verbose, bool withTaskbar, bool withLibraryButton, bool blockExternalLinks); ~Response() = default; int send(const RequestContext& request, MHD_Connection* connection); @@ -57,7 +57,8 @@ class Response { void set_code(int code) { m_returnCode = code; } void set_cache(bool cache) { m_useCache = cache; } void set_compress(bool compress) { m_compress = compress; } - void set_taskbar(const std::string& bookName, const std::string& bookTitle); + void set_taskbar(const std::string& bookName, const std::string& bookTitle) { return set_taskbar(bookName, bookTitle, m_blockExternalLinks); } + void set_taskbar(const std::string& bookName, const std::string& bookTitle, bool blockExternalLinks); void set_range_first(uint64_t start) { m_startRange = start; } void set_range_len(uint64_t len) { m_lenRange = len; } @@ -75,6 +76,7 @@ class Response { int m_returnCode; bool m_withTaskbar; bool m_withLibraryButton; + bool m_blockExternalLinks; bool m_useCache; bool m_compress; bool m_addTaskbar; diff --git a/src/wrapper/java/kiwixserver.cpp b/src/wrapper/java/kiwixserver.cpp index 7242c4cd..1fb2f360 100644 --- a/src/wrapper/java/kiwixserver.cpp +++ b/src/wrapper/java/kiwixserver.cpp @@ -85,6 +85,12 @@ Java_org_kiwix_kiwixlib_JNIKiwixServer_setTaskbar(JNIEnv* env, jobject obj, jboo SERVER->setTaskbar(withTaskbar, withLibraryButton); } +JNIEXPORT void JNICALL +Java_org_kiwix_kiwixlib_JNIKiwixServer_setTaskbar(JNIEnv* env, jobject obj, jboolean withTaskbar, jboolean withLibraryButton, jboolean blockExternalLinks) +{ + SERVER->setTaskbar(withTaskbar, withLibraryButton, blockExternalLinks); +} + JNIEXPORT jboolean JNICALL Java_org_kiwix_kiwixlib_JNIKiwixServer_start(JNIEnv* env, jobject obj) { diff --git a/src/wrapper/java/org/kiwix/kiwixlib/JNIKiwixServer.java b/src/wrapper/java/org/kiwix/kiwixlib/JNIKiwixServer.java index 578e2f4d..2fc49e44 100644 --- a/src/wrapper/java/org/kiwix/kiwixlib/JNIKiwixServer.java +++ b/src/wrapper/java/org/kiwix/kiwixlib/JNIKiwixServer.java @@ -33,6 +33,7 @@ public class JNIKiwixServer public native void setNbThreads(int nbTreads); public native void setTaskbar(boolean withTaskBar, boolean witLibraryButton); + public native void setTaskbar(boolean withTaskBar, boolean witLibraryButton, boolean blockExternalLinks); public native boolean start(); diff --git a/static/resources_list.txt b/static/resources_list.txt index 607f30fe..466e2d39 100644 --- a/static/resources_list.txt +++ b/static/resources_list.txt @@ -28,4 +28,6 @@ templates/index.html templates/suggestion.json templates/head_part.html templates/taskbar_part.html +templates/captured_external.html +templates/block_external.js opensearchdescription.xml diff --git a/static/templates/block_external.js b/static/templates/block_external.js new file mode 100644 index 00000000..21b00508 --- /dev/null +++ b/static/templates/block_external.js @@ -0,0 +1,17 @@ + function capture(e) { $(e.target).attr("href", encodeURI("/external?source=" + e.target.href)); } + jk( document ).ready(function() { + jk("a").on({click: function(e) { + if ("target" in e && "href" in e.target) { + var href = e.target.href; + if (href.indexOf(window.location.origin) == 0) + return; + if (href.substr(0, 2) == "//") + return capture(e); + if (href.substr(0, 5) == "http:") + return capture(e); + if (href.substr(0, 6) == "https:") + return capture(e); + return; + } + }}); + }); diff --git a/static/templates/captured_external.html b/static/templates/captured_external.html new file mode 100644 index 00000000..01617231 --- /dev/null +++ b/static/templates/captured_external.html @@ -0,0 +1,23 @@ + + + + + Welcome to Kiwix Server + + + + + + + + +

External link blocked

+

This instance of Kiwix protects you from accidentaly going to external (out-of ZIM) links.

+

If you intend to go to such locations, please click the link bellow.

+

Go to {{ source }}

+
+ Powered by Kiwix +
+ + + diff --git a/static/templates/head_part.html b/static/templates/head_part.html index 47b7c792..1dcebcec 100644 --- a/static/templates/head_part.html +++ b/static/templates/head_part.html @@ -5,6 +5,7 @@