mirror of
https://github.com/kiwix/libkiwix.git
synced 2025-12-24 06:57:59 -05:00
Compare commits
30 Commits
profile-di
...
9.1.2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9f61301423 | ||
|
|
9c101daad7 | ||
|
|
0586ef6d41 | ||
|
|
8fc42558d3 | ||
|
|
5d98a6964e | ||
|
|
9d8bf8ddcb | ||
|
|
4c8aad0e68 | ||
|
|
eb6f0f710c | ||
|
|
d9d2a702ef | ||
|
|
cbf5bd57a8 | ||
|
|
2bf6b04726 | ||
|
|
12a0660342 | ||
|
|
ab478ed4cf | ||
|
|
533541cf45 | ||
|
|
bab0fd3d4f | ||
|
|
375792a686 | ||
|
|
7155c788e2 | ||
|
|
3ac926b39b | ||
|
|
ea3444e2fe | ||
|
|
083c06d170 | ||
|
|
286fbba9f6 | ||
|
|
778603e1bf | ||
|
|
411d4b0779 | ||
|
|
bce0e8c37c | ||
|
|
507002d34e | ||
|
|
34a8144f51 | ||
|
|
4709a42f4f | ||
|
|
d04d9bf7f3 | ||
|
|
412f0d9c61 | ||
|
|
0ad8bf45fc |
@@ -1,6 +1,6 @@
|
||||
name: CI
|
||||
|
||||
on: [pull_request]
|
||||
on: [push]
|
||||
|
||||
jobs:
|
||||
Macos:
|
||||
@@ -85,25 +85,20 @@ jobs:
|
||||
container:
|
||||
image: "kiwix/kiwix-build_ci:${{matrix.image_variant}}-26"
|
||||
steps:
|
||||
- name: Extract branch name
|
||||
shell: bash
|
||||
run: echo "##[set-output name=branch;]$(echo ${GITHUB_REF#refs/heads/})"
|
||||
id: extract_branch
|
||||
- name: Checkout code
|
||||
shell: python
|
||||
run: |
|
||||
import json
|
||||
from subprocess import check_call
|
||||
from os import environ
|
||||
with open(environ['GITHUB_EVENT_PATH'], 'r') as f:
|
||||
content = f.read()
|
||||
event_data = json.loads(content)
|
||||
try:
|
||||
branch_ref = event_data['ref'].split('/')[-1]
|
||||
except KeyError:
|
||||
branch_ref = event_data['pull_request']['head']['ref']
|
||||
print("Cloning branch", branch_ref)
|
||||
command = [
|
||||
'git', 'clone',
|
||||
'https://github.com/${{github.repository}}',
|
||||
'--depth=1',
|
||||
'--branch', branch_ref
|
||||
'--branch', '${{steps.extract_branch.outputs.branch}}'
|
||||
]
|
||||
check_call(command, cwd=environ['HOME'])
|
||||
- name: Install deps
|
||||
19
ChangeLog
19
ChangeLog
@@ -1,3 +1,22 @@
|
||||
kiwix-lib 9.1.2
|
||||
===============
|
||||
|
||||
* Do not use the pathToSave if it is empty.
|
||||
|
||||
kiwix-lib 9.1.1
|
||||
===============
|
||||
|
||||
* Fix the detection of the dataDirectory on windows.
|
||||
|
||||
kiwix-lib 9.1.0
|
||||
===============
|
||||
|
||||
* [JAVA] Add a method to get the size of an article.
|
||||
* Add a new method to the libray to get the book by path.
|
||||
* Add a option to make the server blocks external link.
|
||||
Links are intercepted by js an redirected to a "portail" page.
|
||||
* [ODPS] Correctly handle book's articleCount and mediaCount.
|
||||
|
||||
kiwix-lib 9.0.1
|
||||
===============
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ GNU/Linux, macOS, Android, iOS, ...).
|
||||
|
||||
[](https://bintray.com/kiwix/kiwix/kiwixlib/_latestVersion)
|
||||
[](https://aur.archlinux.org/packages/kiwix-lib/)
|
||||
[](https://travis-ci.com/kiwix/kiwix-lib)
|
||||
[](https://github.com/kiwix/kiwix-lib/actions?query=branch%3Amaster)
|
||||
[](https://www.codefactor.io/repository/github/kiwix/kiwix-lib)
|
||||
[](https://codecov.io/gh/kiwix/kiwix-lib)
|
||||
[](https://www.gnu.org/licenses/gpl-3.0)
|
||||
@@ -41,7 +41,7 @@ libraries need to be available:
|
||||
* [Aria2](https://aria2.github.io/) (package `aria2` on Ubuntu)
|
||||
* [Mustache](https://github.com/kainjow/Mustache) (Just copy the
|
||||
header `mustache.hpp` somewhere it can be found by the compiler and/or
|
||||
set CPPFLAGS with correct `-I` option)
|
||||
set CPPFLAGS with correct `-I` option). Use Mustache version 3 only.
|
||||
|
||||
These dependencies may or may not be packaged by your operating
|
||||
system. They may also be packaged but only in an older version. The
|
||||
|
||||
@@ -26,7 +26,7 @@ task writePom {
|
||||
project {
|
||||
groupId 'org.kiwix.kiwixlib'
|
||||
artifactId 'kiwixlib'
|
||||
version '9.0.1' + (System.env.KIWIXLIB_BUILDVERSION == null ? '' : '-'+System.env.KIWIXLIB_BUILDVERSION)
|
||||
version '9.1.2' + (System.env.KIWIXLIB_BUILDVERSION == null ? '' : '-'+System.env.KIWIXLIB_BUILDVERSION)
|
||||
packaging 'aar'
|
||||
name 'kiwixlib'
|
||||
url 'https://github.com/kiwix/kiwix-lib'
|
||||
|
||||
@@ -98,7 +98,7 @@ class Book
|
||||
std::string m_id;
|
||||
std::string m_downloadId;
|
||||
std::string m_path;
|
||||
bool m_pathValid;
|
||||
bool m_pathValid = false;
|
||||
std::string m_title;
|
||||
std::string m_description;
|
||||
std::string m_language;
|
||||
@@ -110,10 +110,10 @@ class Book
|
||||
std::string m_flavour;
|
||||
std::string m_tags;
|
||||
std::string m_origId;
|
||||
uint64_t m_articleCount;
|
||||
uint64_t m_mediaCount;
|
||||
bool m_readOnly;
|
||||
uint64_t m_size;
|
||||
uint64_t m_articleCount = 0;
|
||||
uint64_t m_mediaCount = 0;
|
||||
bool m_readOnly = false;
|
||||
uint64_t m_size = 0;
|
||||
mutable std::string m_favicon;
|
||||
std::string m_faviconUrl;
|
||||
std::string m_faviconMimeType;
|
||||
|
||||
@@ -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; }
|
||||
{ m_withTaskbar = withTaskbar; m_withLibraryButton = withLibraryButton; }
|
||||
void setBlockExternalLinks(bool blockExternalLinks)
|
||||
{ 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<InternalServer> mp_server;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
project('kiwix-lib', 'cpp',
|
||||
version : '9.0.1', # Also change this in android-kiwix-lib-publisher/kiwixLibAndroid/build.gradle
|
||||
version : '9.1.2', # Also change this in android-kiwix-lib-publisher/kiwixLibAndroid/build.gradle
|
||||
license : 'GPL',
|
||||
default_options : ['c_std=c11', 'cpp_std=c++11', 'werror=true'])
|
||||
|
||||
@@ -20,7 +20,7 @@ endif
|
||||
|
||||
thread_dep = dependency('threads')
|
||||
libicu_dep = dependency('icu-i18n', static:static_deps)
|
||||
libzim_dep = dependency('libzim', version : '>=5.0.0', static:static_deps)
|
||||
libzim_dep = dependency('libzim', version : '>=6.1.1', static:static_deps)
|
||||
pugixml_dep = dependency('pugixml', static:static_deps)
|
||||
libcurl_dep = dependency('libcurl', static:static_deps)
|
||||
microhttpd_dep = dependency('libmicrohttpd', static:static_deps)
|
||||
|
||||
@@ -156,6 +156,8 @@ void Book::updateFromOpds(const pugi::xml_node& node, const std::string& urlHost
|
||||
m_name = VALUE("name");
|
||||
m_flavour = VALUE("flavour");
|
||||
m_tags = VALUE("tags");
|
||||
m_articleCount = strtoull(VALUE("articleCount"), 0, 0);
|
||||
m_mediaCount = strtoull(VALUE("mediaCount"), 0, 0);
|
||||
for(auto linkNode = node.child("link"); linkNode;
|
||||
linkNode = linkNode.next_sibling("link")) {
|
||||
std::string rel = linkNode.attribute("rel").value();
|
||||
|
||||
@@ -175,7 +175,7 @@ std::string Manager::addBookFromPathAndGetId(const std::string& pathToOpen,
|
||||
kiwix::Book book;
|
||||
|
||||
if (this->readBookFromPath(pathToOpen, &book)) {
|
||||
if (pathToSave != pathToOpen) {
|
||||
if (!pathToSave.empty() && pathToSave != pathToOpen) {
|
||||
book.setPath(isRelativePath(pathToSave)
|
||||
? computeAbsolutePath(
|
||||
removeLastPathElement(writableLibraryPath),
|
||||
|
||||
@@ -78,6 +78,8 @@ pugi::xml_node OPDSDumper::handleBook(Book book, pugi::xml_node root_node) {
|
||||
ADD_TEXT_ENTRY(entry_node, "name", book.getName());
|
||||
ADD_TEXT_ENTRY(entry_node, "flavour", book.getFlavour());
|
||||
ADD_TEXT_ENTRY(entry_node, "tags", book.getTags());
|
||||
ADD_TEXT_ENTRY(entry_node, "articleCount", to_string(book.getArticleCount()));
|
||||
ADD_TEXT_ENTRY(entry_node, "mediaCount", to_string(book.getMediaCount()));
|
||||
ADD_TEXT_ENTRY(entry_node, "icon", rootLocation + "/meta?name=favicon&content=" + book.getHumanReadableIdFromPath());
|
||||
|
||||
auto content_node = entry_node.append_child("link");
|
||||
|
||||
@@ -714,7 +714,6 @@ bool Reader::searchSuggestions(const string& prefix,
|
||||
const bool reset)
|
||||
{
|
||||
bool retVal = false;
|
||||
zim::File::const_iterator articleItr;
|
||||
|
||||
/* Reset the suggestions otherwise check if the suggestions number is less
|
||||
* than the suggestionsCount */
|
||||
@@ -732,7 +731,7 @@ bool Reader::searchSuggestions(const string& prefix,
|
||||
return false;
|
||||
}
|
||||
|
||||
for (articleItr = zimFileHandler->findByTitle('A', prefix);
|
||||
for (auto articleItr = zimFileHandler->findByTitle('A', prefix);
|
||||
articleItr != zimFileHandler->end()
|
||||
&& articleItr->getTitle().compare(0, prefix.size(), prefix) == 0
|
||||
&& this->suggestions.size() < suggestionsCount;
|
||||
|
||||
@@ -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() == "/catch/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("", "");
|
||||
return response;
|
||||
}
|
||||
|
||||
Response InternalServer::handle_catalog(const RequestContext& request)
|
||||
{
|
||||
if (m_verbose.load()) {
|
||||
|
||||
@@ -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(""),
|
||||
@@ -127,6 +128,17 @@ void Response::introduce_taskbar()
|
||||
}
|
||||
|
||||
|
||||
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,
|
||||
"<head>",
|
||||
script_tag);
|
||||
}
|
||||
|
||||
|
||||
int Response::send(const RequestContext& request, MHD_Connection* connection)
|
||||
{
|
||||
@@ -136,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
|
||||
|
||||
@@ -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);
|
||||
@@ -64,6 +64,7 @@ class Response {
|
||||
int getReturnCode() { return m_returnCode; }
|
||||
|
||||
void introduce_taskbar();
|
||||
void inject_externallinks_blocker();
|
||||
|
||||
private:
|
||||
bool m_verbose;
|
||||
@@ -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;
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
*/
|
||||
|
||||
#include "tools/pathTools.h"
|
||||
#include <stdexcept>
|
||||
|
||||
#ifdef __APPLE__
|
||||
#include <limits.h>
|
||||
@@ -42,12 +43,16 @@
|
||||
#include <algorithm>
|
||||
|
||||
#ifdef _WIN32
|
||||
#define SEPARATOR "\\"
|
||||
# define SEPARATOR "\\"
|
||||
# include <io.h>
|
||||
#else
|
||||
#define SEPARATOR "/"
|
||||
#include <unistd.h>
|
||||
# define SEPARATOR "/"
|
||||
# include <unistd.h>
|
||||
# include <sys/stat.h>
|
||||
#endif
|
||||
|
||||
#include <fcntl.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#ifndef PATH_MAX
|
||||
@@ -227,14 +232,33 @@ std::string getFileSizeAsString(const std::string& path)
|
||||
|
||||
std::string getFileContent(const std::string& path)
|
||||
{
|
||||
std::ifstream f(path, std::ios::in|std::ios::ate);
|
||||
#ifdef _WIN32
|
||||
auto wpath = Utf8ToWide(path);
|
||||
auto fd = _wopen(wpath.c_str(), _O_RDONLY | _O_BINARY);
|
||||
#else
|
||||
auto fd = open(path.c_str(), O_RDONLY);
|
||||
#endif
|
||||
std::string content;
|
||||
if (f.is_open()) {
|
||||
auto size = f.tellg();
|
||||
content.reserve(size);
|
||||
f.seekg(0, std::ios::beg);
|
||||
content.assign((std::istreambuf_iterator<char>(f)),
|
||||
std::istreambuf_iterator<char>());
|
||||
if (fd != -1) {
|
||||
#ifdef _WIN32
|
||||
auto size = _lseeki64(fd, 0, SEEK_END);
|
||||
#else
|
||||
auto size = lseek(fd, 0, SEEK_END);
|
||||
#endif
|
||||
content.resize(size);
|
||||
#ifdef _WIN32
|
||||
_lseeki64(fd, 0, SEEK_SET);
|
||||
#else
|
||||
lseek(fd, 0, SEEK_SET);
|
||||
#endif
|
||||
auto p = (char*)content.data();
|
||||
while (size) {
|
||||
auto readsize = size > 2048 ? 2048 : size;
|
||||
readsize = ::read(fd, p, readsize);
|
||||
p += readsize;
|
||||
size -= readsize;
|
||||
}
|
||||
close(fd);
|
||||
}
|
||||
return content;
|
||||
}
|
||||
@@ -287,22 +311,22 @@ std::string makeTmpDirectory()
|
||||
/* Try to create a link and if does not work then make a copy */
|
||||
bool copyFile(const std::string& sourcePath, const std::string& destPath)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
return CopyFileW(Utf8ToWide(sourcePath).c_str(), Utf8ToWide(destPath).c_str(), 1);
|
||||
#else
|
||||
try {
|
||||
#ifndef _WIN32
|
||||
if (link(sourcePath.c_str(), destPath.c_str()) != 0) {
|
||||
#endif
|
||||
std::ifstream infile(sourcePath.c_str(), std::ios_base::binary);
|
||||
std::ofstream outfile(destPath.c_str(), std::ios_base::binary);
|
||||
outfile << infile.rdbuf();
|
||||
#ifndef _WIN32
|
||||
}
|
||||
#endif
|
||||
} catch (std::exception& e) {
|
||||
std::cerr << e.what() << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
std::string getExecutablePath(bool realPathOnly)
|
||||
@@ -341,10 +365,21 @@ std::string getExecutablePath(bool realPathOnly)
|
||||
|
||||
bool writeTextFile(const std::string& path, const std::string& content)
|
||||
{
|
||||
std::ofstream file;
|
||||
file.open(path.c_str());
|
||||
file << content;
|
||||
file.close();
|
||||
#ifdef _WIN32
|
||||
auto wpath = Utf8ToWide(path);
|
||||
auto fd = _wopen(wpath.c_str(), _O_WRONLY | _O_CREAT | _O_TRUNC, S_IWRITE);
|
||||
#else
|
||||
auto fd = open(path.c_str(), O_WRONLY | O_CREAT | O_TRUNC,
|
||||
S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
|
||||
#endif
|
||||
if (fd == -1)
|
||||
return false;
|
||||
|
||||
if (write(fd, content.c_str(), content.size()) != (long)content.size()) {
|
||||
close(fd);
|
||||
return false;
|
||||
}
|
||||
close(fd);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -364,30 +399,48 @@ std::string getCurrentDirectory()
|
||||
|
||||
std::string getDataDirectory()
|
||||
{
|
||||
// Try to get the dataDir from the `KIWIX_DATA_DIR` env var
|
||||
#ifdef _WIN32
|
||||
char* cDataDir = ::getenv("APPDATA");
|
||||
wchar_t* cDataDir = ::_wgetenv(L"KIWIX_DATA_DIR");
|
||||
if (cDataDir != nullptr) {
|
||||
return WideToUtf8(cDataDir);
|
||||
}
|
||||
#else
|
||||
char* cDataDir = ::getenv("KIWIX_DATA_DIR");
|
||||
#endif
|
||||
std::string dataDir = cDataDir==nullptr ? "" : cDataDir;
|
||||
if (!dataDir.empty()) {
|
||||
return dataDir;
|
||||
if (cDataDir != nullptr) {
|
||||
return cDataDir;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Compute the dataDir from the user directory.
|
||||
std::string dataDir;
|
||||
#ifdef _WIN32
|
||||
cDataDir = ::getenv("USERPROFILE");
|
||||
dataDir = cDataDir==nullptr ? getCurrentDirectory() : cDataDir;
|
||||
cDataDir = ::_wgetenv(L"APPDATA");
|
||||
if (cDataDir == nullptr)
|
||||
cDataDir = ::_wgetenv(L"USERPROFILE");
|
||||
if (cDataDir != nullptr)
|
||||
dataDir = WideToUtf8(cDataDir);
|
||||
#else
|
||||
cDataDir = ::getenv("XDG_DATA_HOME");
|
||||
dataDir = cDataDir==nullptr ? "" : cDataDir;
|
||||
if (dataDir.empty()) {
|
||||
if (cDataDir != nullptr) {
|
||||
dataDir = cDataDir;
|
||||
} else {
|
||||
cDataDir = ::getenv("HOME");
|
||||
dataDir = cDataDir==nullptr ? getCurrentDirectory() : cDataDir;
|
||||
dataDir = appendToDirectory(dataDir, ".local");
|
||||
dataDir = appendToDirectory(dataDir, "share");
|
||||
if (cDataDir != nullptr) {
|
||||
dataDir = cDataDir;
|
||||
dataDir = appendToDirectory(dataDir, ".local");
|
||||
dataDir = appendToDirectory(dataDir, "share");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
auto ret = appendToDirectory(dataDir, "kiwix");
|
||||
return ret;
|
||||
if (!dataDir.empty()) {
|
||||
dataDir = appendToDirectory(dataDir, "kiwix");
|
||||
makeDirectory(dataDir);
|
||||
return dataDir;
|
||||
}
|
||||
|
||||
// Let's use the currentDirectory
|
||||
return getCurrentDirectory();
|
||||
}
|
||||
|
||||
static std::map<std::string, std::string> extMimeTypes = {
|
||||
|
||||
@@ -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_setBlockExternalLinks(JNIEnv* env, jobject obj, jboolean blockExternalLinks)
|
||||
{
|
||||
SERVER->setBlockExternalLinks(blockExternalLinks);
|
||||
}
|
||||
|
||||
JNIEXPORT jboolean JNICALL
|
||||
Java_org_kiwix_kiwixlib_JNIKiwixServer_start(JNIEnv* env, jobject obj)
|
||||
{
|
||||
|
||||
@@ -34,6 +34,8 @@ public class JNIKiwixServer
|
||||
|
||||
public native void setTaskbar(boolean withTaskBar, boolean witLibraryButton);
|
||||
|
||||
public native void setBlockExternalLinks(boolean blockExternalLinks);
|
||||
|
||||
public native boolean start();
|
||||
|
||||
public native void stop();
|
||||
|
||||
@@ -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,4 +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
|
||||
opensearchdescription.xml
|
||||
|
||||
72
static/skin/block_external.js
Normal file
72
static/skin/block_external.js
Normal file
@@ -0,0 +1,72 @@
|
||||
var block_path = "/catch/external";
|
||||
// called only on external links
|
||||
function capture_event(e, target) { target.setAttribute("href", encodeURI(block_path + "?source=" + target.href)); }
|
||||
|
||||
// called on all link clicks. filters external and call capture_event
|
||||
function on_click_event(e) {
|
||||
var target = findParent("a", e.target);
|
||||
if (target !== null && "href" in target) {
|
||||
var href = target.href;
|
||||
if (window.location.pathname.indexOf(block_path) == 0) // already in catch page
|
||||
return;
|
||||
if (href.indexOf(window.location.origin) == 0)
|
||||
return;
|
||||
if (href.substr(0, 2) == "//")
|
||||
return capture_event(e, target);
|
||||
if (href.substr(0, 5) == "http:")
|
||||
return capture_event(e, target);
|
||||
if (href.substr(0, 6) == "https:")
|
||||
return capture_event(e, target);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// script entrypoint (called on document ready)
|
||||
function run() { live('a', 'click', on_click_event); }
|
||||
|
||||
// find first parent with tagname
|
||||
function findParent(tagname, el) {
|
||||
while (el) {
|
||||
if ((el.nodeName || el.tagName).toLowerCase() === tagname.toLowerCase()) {
|
||||
return el;
|
||||
}
|
||||
el = el.parentNode;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// 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();
|
||||
});
|
||||
14
static/templates/captured_external.html
Normal file
14
static/templates/captured_external.html
Normal file
@@ -0,0 +1,14 @@
|
||||
<!DOCTYPE html>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>External link blocked</title>
|
||||
</head>
|
||||
<body class="kiwix">
|
||||
<h1>External link blocked</h1>
|
||||
<p>This instance of Kiwix protects you from accidentaly going to external (out-of ZIM) links.</p>
|
||||
<p>If you intend to go to such locations, please click the link below.</p>
|
||||
<p><a href="{{ source }}">Go to {{ source }}</a></p>
|
||||
<div id="kiwixfooter">Powered by <a href="https://kiwix.org">Kiwix</a></div>
|
||||
</body>
|
||||
</html>
|
||||
1
static/templates/external_blocker_part.html
Normal file
1
static/templates/external_blocker_part.html
Normal file
@@ -0,0 +1 @@
|
||||
<script type="text/javascript" src="{{root}}/skin/block_external.js"></script>
|
||||
Reference in New Issue
Block a user