diff --git a/include/server.h b/include/server.h index 5b439c309..b38ad8c04 100644 --- a/include/server.h +++ b/include/server.h @@ -56,9 +56,9 @@ namespace kiwix void setNbThreads(int threads) { m_nbThreads = threads; } void setVerbose(bool verbose) { m_verbose = verbose; } void setTaskbar(bool withTaskbar, bool withLibraryButton) - { setTaskbar(withTaskbar, withLibraryButton, m_blockExternalLinks); } - void setTaskbar(bool withTaskbar, bool withLibraryButton, bool blockExternalLinks) - { m_withTaskbar = withTaskbar; m_withLibraryButton = withLibraryButton; m_blockExternalLinks = blockExternalLinks; } + { m_withTaskbar = withTaskbar; m_withLibraryButton = withLibraryButton; } + void setBlockExternalLinks(bool blockExternalLinks) + { m_blockExternalLinks = blockExternalLinks; } protected: Library* mp_library; diff --git a/src/server.cpp b/src/server.cpp index 50a30748c..c4cf4a546 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -346,7 +346,7 @@ Response InternalServer::handle_request(const RequestContext& request) if (request.get_url() == "/random") return handle_random(request); - if (request.get_url() == "/external") + if (request.get_url() == "/catch/external") return handle_captured_external(request); return handle_content(request); @@ -732,11 +732,11 @@ Response InternalServer::handle_captured_external(const RequestContext& request) auto data = get_default_data(); data.set("source", source); - auto response = get_default_response(); + Response response = Response(m_root, m_verbose.load(), m_withTaskbar, m_withLibraryButton, false); 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); + response.set_taskbar("", ""); return response; } diff --git a/src/server/response.cpp b/src/server/response.cpp index 4344de7e1..fdf1d80f6 100644 --- a/src/server/response.cpp +++ b/src/server/response.cpp @@ -117,7 +117,7 @@ void Response::introduce_taskbar() auto head_content = render_template(RESOURCE::templates::head_part_html, data); m_content = appendToFirstOccurence( m_content, - "", + "\n", head_content); auto taskbar_part = render_template(RESOURCE::templates::taskbar_part_html, data); @@ -125,17 +125,20 @@ 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); - } } +void Response::inject_externallinks_blocker() +{ + kainjow::mustache::data data; + data.set("root", m_root); + auto script_tag = render_template(RESOURCE::templates::external_blocker_part_html, data); + m_content = appendToFirstOccurence( + m_content, + "\n", + script_tag); +} + int Response::send(const RequestContext& request, MHD_Connection* connection) { @@ -145,6 +148,9 @@ int Response::send(const RequestContext& request, MHD_Connection* connection) if (m_addTaskbar) { introduce_taskbar(); } + if ( m_blockExternalLinks ) { + inject_externallinks_blocker(); + } bool shouldCompress = m_compress && request.can_compress(); shouldCompress &= m_mimeType.find("text/") != string::npos @@ -248,12 +254,11 @@ void Response::set_entry(const Entry& entry) { m_mode = ResponseMode::ENTRY; } -void Response::set_taskbar(const std::string& bookName, const std::string& bookTitle, bool blockExternalLinks) +void Response::set_taskbar(const std::string& bookName, const std::string& bookTitle) { 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 0340b013a..89f235072 100644 --- a/src/server/response.h +++ b/src/server/response.h @@ -57,14 +57,14 @@ 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) { return set_taskbar(bookName, bookTitle, m_blockExternalLinks); } - void set_taskbar(const std::string& bookName, const std::string& bookTitle, bool blockExternalLinks); + void set_taskbar(const std::string& bookName, const std::string& bookTitle); void set_range_first(uint64_t start) { m_startRange = start; } void set_range_len(uint64_t len) { m_lenRange = len; } int getReturnCode() { return m_returnCode; } void introduce_taskbar(); + void inject_externallinks_blocker(); private: bool m_verbose; diff --git a/src/wrapper/java/kiwixserver.cpp b/src/wrapper/java/kiwixserver.cpp index 1fb2f360f..b64f61899 100644 --- a/src/wrapper/java/kiwixserver.cpp +++ b/src/wrapper/java/kiwixserver.cpp @@ -86,9 +86,9 @@ Java_org_kiwix_kiwixlib_JNIKiwixServer_setTaskbar(JNIEnv* env, jobject obj, jboo } JNIEXPORT void JNICALL -Java_org_kiwix_kiwixlib_JNIKiwixServer_setTaskbar(JNIEnv* env, jobject obj, jboolean withTaskbar, jboolean withLibraryButton, jboolean blockExternalLinks) +Java_org_kiwix_kiwixlib_JNIKiwixServer_setBlockExternalLinks(JNIEnv* env, jobject obj, jboolean blockExternalLinks) { - SERVER->setTaskbar(withTaskbar, withLibraryButton, blockExternalLinks); + SERVER->setBlockExternalLinks(blockExternalLinks); } JNIEXPORT jboolean JNICALL diff --git a/src/wrapper/java/org/kiwix/kiwixlib/JNIKiwixServer.java b/src/wrapper/java/org/kiwix/kiwixlib/JNIKiwixServer.java index 2fc49e449..11c5f0d70 100644 --- a/src/wrapper/java/org/kiwix/kiwixlib/JNIKiwixServer.java +++ b/src/wrapper/java/org/kiwix/kiwixlib/JNIKiwixServer.java @@ -33,7 +33,8 @@ 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 void setBlockExternalLinks(boolean blockExternalLinks); public native boolean start(); diff --git a/static/resources_list.txt b/static/resources_list.txt index 466e2d39d..7579c33f6 100644 --- a/static/resources_list.txt +++ b/static/resources_list.txt @@ -20,6 +20,7 @@ skin/jquery-ui/jquery-ui.min.css skin/caret.png skin/taskbar.js skin/taskbar.css +skin/block_external.js templates/search_result.html templates/no_search_result.html templates/404.html @@ -28,6 +29,6 @@ templates/index.html templates/suggestion.json templates/head_part.html templates/taskbar_part.html +templates/external_blocker_part.html templates/captured_external.html -templates/block_external.js opensearchdescription.xml diff --git a/static/skin/block_external.js b/static/skin/block_external.js new file mode 100644 index 000000000..9b70f243c --- /dev/null +++ b/static/skin/block_external.js @@ -0,0 +1,57 @@ +// called only on external links +function capture_event(e) { e.target.setAttribute("href", encodeURI("/catch/external?source=" + e.target.href)); } + +// called on all link clicks. filters external and call capture_event +function on_click_event(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_event(e); + if (href.substr(0, 5) == "http:") + return capture_event(e); + if (href.substr(0, 6) == "https:") + return capture_event(e); + return; + } +} + +// script entrypoint (called on document ready) +function run() { live('a', 'click', on_click_event); } + +// matches polyfill +this.Element && function(ElementPrototype) { + ElementPrototype.matches = ElementPrototype.matches || + ElementPrototype.matchesSelector || + ElementPrototype.webkitMatchesSelector || + ElementPrototype.msMatchesSelector || + function(selector) { + var node = this, nodes = (node.parentNode || node.document).querySelectorAll(selector), i = -1; + while (nodes[++i] && nodes[i] != node); + return !!nodes[i]; + } +}(Element.prototype); + +// helper for enabling IE 8 event bindings +function addEvent(el, type, handler) { + if (el.attachEvent) el.attachEvent('on'+type, handler); else el.addEventListener(type, handler); +} + +// live binding helper using matchesSelector +function live(selector, event, callback, context) { + addEvent(context || document, event, function(e) { + var found, el = e.target || e.srcElement; + while (el && el.matches && el !== context && !(found = el.matches(selector))) el = el.parentElement; + if (found) callback.call(el, e); + }); +} + +// in case the document is already rendered +if (document.readyState!='loading') run(); +// modern browsers +else if (document.addEventListener) document.addEventListener('DOMContentLoaded', run); +// IE <= 8 +else document.attachEvent('onreadystatechange', function(){ + if (document.readyState=='complete') run(); +}); diff --git a/static/templates/block_external.js b/static/templates/block_external.js deleted file mode 100644 index 21b005084..000000000 --- a/static/templates/block_external.js +++ /dev/null @@ -1,17 +0,0 @@ - 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/external_blocker_part.html b/static/templates/external_blocker_part.html new file mode 100644 index 000000000..6bb0a0c21 --- /dev/null +++ b/static/templates/external_blocker_part.html @@ -0,0 +1 @@ + diff --git a/static/templates/head_part.html b/static/templates/head_part.html index 1dcebceca..47b7c7921 100644 --- a/static/templates/head_part.html +++ b/static/templates/head_part.html @@ -5,7 +5,6 @@