1 Commits

Author SHA1 Message Date
Matthieu Gautier
17edba1d02 Adapt kiwix-tools to new libkiwix API. 2023-07-16 07:50:45 +02:00
19 changed files with 517 additions and 660 deletions

View File

@@ -1,106 +1,61 @@
name: CI
on:
pull_request:
push:
branches:
- main
on: [push]
jobs:
Windows:
runs-on: windows-2022
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Python 3.10
uses: actions/setup-python@v5
with:
python-version: '3.10'
- name: Install packages
run:
choco install pkgconfiglite ninja
- name: Install python modules
run: pip3 install meson
- name: Setup MSVC compiler
uses: bus1/cabuild/action/msdevshell@v1
with:
architecture: x64
- name: Install dependencies
uses: kiwix/kiwix-build/actions/dl_deps_archive@main
with:
target_platform: win-x86_64-static
- name: Compile
shell: cmd
run: |
set PKG_CONFIG_PATH=%cd%\BUILD_win-amd64\INSTALL\lib\pkgconfig
set CPPFLAGS=-I%cd%\BUILD_win-amd64\INSTALL\include
meson.exe setup . build -Dstatic-linkage=true --buildtype=release
cd build
ninja.exe
- name: Test
shell: cmd
run: |
cd build
meson.exe test --verbose
env:
WAIT_TIME_FACTOR_TEST: 10
Linux:
runs-on: ubuntu-22.04
strategy:
fail-fast: false
matrix:
target:
- linux-x86_64-static
- linux-x86_64-dyn
- native_static
- native_dyn
- win32_static
- win32_dyn
include:
- target: linux-x86_64-static
image_variant: jammy
- target: native_static
image_variant: bionic
lib_postfix: '/x86_64-linux-gnu'
arch_name: linux-x86_64
- target: linux-x86_64-dyn
image_variant: jammy
- target: native_dyn
image_variant: bionic
lib_postfix: '/x86_64-linux-gnu'
arch_name: linux-x86_64
- target: win32_static
image_variant: f35
lib_postfix: '64'
- target: win32_dyn
image_variant: f35
lib_postfix: '64'
env:
HOME: /home/runner
runs-on: ubuntu-latest
container:
image: "ghcr.io/kiwix/kiwix-build_ci_${{matrix.image_variant}}:2025-06-07"
image: "ghcr.io/kiwix/kiwix-build_ci_${{matrix.image_variant}}:36"
steps:
- name: Extract branch name
shell: bash
run: echo "##[set-output name=branch;]$(echo ${GITHUB_REF#refs/heads/})"
id: extract_branch
- name: Checkout code
uses: actions/checkout@v4
- name: Install dependencies
uses: kiwix/kiwix-build/actions/dl_deps_archive@main
with:
target_platform: ${{ matrix.target }}
uses: actions/checkout@v3
- name: Install deps
shell: bash
run: |
ARCHIVE_NAME=deps2_${OS_NAME}_${{matrix.target}}_kiwix-tools.tar.xz
wget -O- http://tmp.kiwix.org/ci/${ARCHIVE_NAME} | tar -xJ -C ${{env.HOME}}
- name: Compile
shell: bash
run: |
if [[ "${{matrix.target}}" =~ .*-static ]]; then
if [[ "${{matrix.target}}" =~ .*_static ]]; then
MESON_OPTION="-Dstatic-linkage=true"
else
MESON_OPTION=""
fi
if [ -e "$HOME/BUILD_${{matrix.arch_name}}/meson_cross_file.txt" ]; then
MESON_OPTION="$MESON_OPTION --cross-file $HOME/BUILD_${{matrix.arch_name}}/meson_cross_file.txt"
if [[ ! "${{matrix.target}}" =~ native_.* ]]; then
MESON_OPTION="$MESON_OPTION --cross-file $HOME/BUILD_${{matrix.target}}/meson_cross_file.txt"
fi
meson . build ${MESON_OPTION}
cd build
ninja
env:
PKG_CONFIG_PATH: "${{env.HOME}}/BUILD_${{matrix.arch_name}}/INSTALL/lib/pkgconfig:${{env.HOME}}/BUILD_${{matrix.arch_name}}/INSTALL/lib${{matrix.lib_postfix}}/pkgconfig"
CPPFLAGS: "-I${{env.HOME}}/BUILD_${{matrix.arch_name}}/INSTALL/include"
PKG_CONFIG_PATH: "${{env.HOME}}/BUILD_${{matrix.target}}/INSTALL/lib/pkgconfig:${{env.HOME}}/BUILD_${{matrix.target}}/INSTALL/lib${{matrix.lib_postfix}}/pkgconfig"
CPPFLAGS: "-I${{env.HOME}}/BUILD_${{matrix.target}}/INSTALL/include"

View File

@@ -13,7 +13,7 @@ jobs:
name: Deploy kiwix-tools Docker Image
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v3.4.0
- name: build and publish kiwix-tools
uses: openzim/docker-publish-action@v10
with:
@@ -42,7 +42,7 @@ jobs:
runs-on: ubuntu-22.04
needs: build-and-push-kiwix-tools
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v3.4.0
- name: build and publish kiwix-serve
uses: openzim/docker-publish-action@v10
with:

View File

@@ -1,28 +1,18 @@
name: Packages
on:
pull_request:
push:
branches:
- main
release:
types: [published]
on: [push, pull_request]
jobs:
build-deb:
runs-on: ubuntu-22.04
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
distro:
# - debian-unstable
# - debian-trixie
# - debian-bookworm
# - debian-bullseye
- ubuntu-noble
- ubuntu-kinetic
- ubuntu-jammy
- ubuntu-focal
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v3
# Determine which PPA we should upload to
- name: PPA
@@ -30,55 +20,19 @@ jobs:
run: |
if [[ $REF == refs/tags* ]]
then
echo "ppa=kiwixteam/release" >> $GITHUB_OUTPUT
echo "::set-output name=ppa::kiwixteam/release"
else
echo "ppa=kiwixteam/dev" >> $GITHUB_OUTPUT
echo "::set-output name=ppa::kiwixteam/dev"
fi
env:
REF: ${{ github.ref }}
- uses: legoktm/gh-action-auto-dch@main
- uses: legoktm/gh-action-auto-dch@master
with:
fullname: Kiwix builder
email: release+launchpad@kiwix.org
distro: ${{ matrix.distro }}
# - uses: legoktm/gh-action-build-deb@debian-unstable
# if: matrix.distro == 'debian-unstable'
# name: Build package for debian-unstable
# id: build-debian-unstable
# with:
# args: --no-sign
#
# - uses: legoktm/gh-action-build-deb@b47978ba8498dc8b8153cc3b5f99a5fc1afa5de1 # pin@debian-trixie
# if: matrix.distro == 'debian-trixie'
# name: Build package for debian-trixie
# id: build-debian-trixie
# with:
# args: --no-sign
#
# - uses: legoktm/gh-action-build-deb@1f4e86a6bb34aaad388167eaf5eb85d553935336 # pin@debian-bookworm
# if: matrix.distro == 'debian-bookworm'
# name: Build package for debian-bookworm
# id: build-debian-bookworm
# with:
# args: --no-sign
#
# - uses: legoktm/gh-action-build-deb@084b4263209252ec80a75d2c78a586192c17f18d # pin@debian-bullseye
# if: matrix.distro == 'debian-bullseye'
# name: Build package for debian-bullseye
# id: build-debian-bullseye
# with:
# args: --no-sign
- uses: legoktm/gh-action-build-deb@9114a536498b65c40b932209b9833aa942bf108d # pin@ubuntu-noble
if: matrix.distro == 'ubuntu-noble'
name: Build package for ubuntu-noble
id: build-ubuntu-noble
with:
args: --no-sign
ppa: ${{ steps.ppa.outputs.ppa }}
- uses: legoktm/gh-action-build-deb@ubuntu-jammy
if: matrix.distro == 'ubuntu-jammy'
name: Build package for ubuntu-jammy
@@ -87,12 +41,28 @@ jobs:
args: --no-sign
ppa: ${{ steps.ppa.outputs.ppa }}
- uses: actions/upload-artifact@v4
- uses: legoktm/gh-action-build-deb@ubuntu-kinetic
if: matrix.distro == 'ubuntu-kinetic'
name: Build package for ubuntu-kinetic
id: build-ubuntu-kinetic
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
id: build-ubuntu-focal
with:
args: --no-sign
ppa: ${{ steps.ppa.outputs.ppa }}
- uses: actions/upload-artifact@v3
with:
name: Packages for ${{ matrix.distro }}
path: output
- uses: legoktm/gh-action-dput@main
- uses: legoktm/gh-action-dput@master
name: Upload dev package
# Only upload on pushes to git default branch
if: github.event_name == 'push' && github.event.ref == 'refs/heads/main' && startswith(matrix.distro, 'ubuntu-')
@@ -101,9 +71,9 @@ jobs:
repository: ppa:kiwixteam/dev
packages: output/*_source.changes
- uses: legoktm/gh-action-dput@main
- uses: legoktm/gh-action-dput@master
name: Upload release package
# Only upload on pushes to main or tag
# Only upload on pushes to master or tag
if: github.event_name == 'push' && startsWith(github.event.ref, 'refs/tags') && startswith(matrix.distro, 'ubuntu-')
with:
gpg_key: ${{ secrets.LAUNCHPAD_GPG }}

View File

@@ -1,21 +0,0 @@
# Read the Docs configuration file
# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details
# Required
version: 2
# Set the version of Python and other tools you might need
build:
os: ubuntu-22.04
tools:
python: "3.11"
# Build documentation in the docs/ directory with Sphinx
sphinx:
configuration: docs/conf.py
# We recommend specifying your dependencies to enable reproducible builds:
# https://docs.readthedocs.io/en/stable/guides/reproducible-builds.html
python:
install:
- requirements: docs/requirements.txt

View File

@@ -1 +0,0 @@
https://kiwix.org/funding.json

View File

@@ -1,46 +1,7 @@
kiwix-tools 3.8.1
=================
Unreleased
==========
* Kiwix server
- Hide port number in URL when server is running on port 80 (@vighnesh-sawant #763)
- Better deal with container /data dir permissions (@kelson42 #787)
* Other
- Fix kiwix-manage docopt integration (@kelson42 #783)
kiwix-tools 3.8.0
=================
* Kiwix server
- Improve message when server is running on standard port (@vighnesh-sawant #763)
- Update container base image to latest Alpline version (@yashgoyal0110 #771)
- Container image to use unprivileged user (@Sedetisu #755)
- Empty urlRootLocation doesn't disable book preview links anymore (@veloman-yunkan #1224)
- Add support of IPv6 (@sgourdas #673 #704)
- Popups are allowed to escape the browser sandbox (@veloman-yunkan #1208)
* Other
- Stop publishing on Ubuntu 20.04 PPA (@kelson42 #748)
- Use Docopt for command line argument parsing (@mgautierfr #695)
* Compilation & Packaging
- Based on libkiwix 14.1.0 (@kelson42 #773)
- Improve CI/CD (@kelson42 #681 #698 #702 #705 #720 #722, @mgautierfr #695)
kiwix-tools 3.7.0
=================
* Fixed ZIM name vs Book name confusion in documentation (@veloman-yunkan #663)
* Fixes compilation dependencies to rely on appropriate version (@kelson42 #667)
* New --skipInvalid Kiwix Server command line option (@schuellerf @kelson42 #666)
kiwix-tools 3.6.0
=================
* Improved kiwix-serve man page (@iArchitSharma #626)
* C++17 compliant code base (@mgautierfr #636)
* Support of libkiwix13 (@mgautierfr #633)
* Additional docker images archs for armv6 and i386 (@rgaudin #622)
* Additional docker images archs for armv6 and i386.
kiwix-tools 3.5.0
=================

View File

@@ -4,14 +4,15 @@ Kiwix tools
The Kiwix tools is a collection of [Kiwix](https://kiwix.org) related
command line tools:
* kiwix-manage: Manage XML based library of ZIM files
* kiwix-search: Full text search in ZIM files
* kiwix-search: Fulltext search in ZIM files
* kiwix-serve: HTTP daemon serving ZIM files
[![latest release](https://img.shields.io/github/v/tag/kiwix/kiwix-tools?label=latest%20release&sort=semver)](https://download.kiwix.org/release/kiwix-tools/)
[![Repositories](https://img.shields.io/repology/repositories/kiwix-tools?label=repositories)](https://github.com/kiwix/kiwix-tools/wiki/Repology)
[![Docker](https://ghcr-badge.egpl.dev/kiwix/kiwix-tools/latest_tag?label=docker)](https://ghcr.io/kiwix/kiwix-tools)
[![Docker](https://ghcr-badge.egpl.dev/kiwix/kiwix-tools/latest_tag?label=docker%20(kiwix-serve))](https://ghcr.io/kiwix/kiwix-tools)
[![Build Status](https://github.com/kiwix/kiwix-tools/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/kiwix/kiwix-tools/actions/workflows/ci.yml?query=branch%3Amain)
[![Docker](https://ghcr-badge.deta.dev/kiwix/kiwix-tools/latest_tag?label=docker)](https://ghcr.io/kiwix/kiwix-tools)
[![Docker](https://ghcr-badge.deta.dev/kiwix/kiwix-tools/latest_tag?label=docker%20(kiwix-serve))](https://ghcr.io/kiwix/kiwix-tools)
[![Sandstorm](https://img.shields.io/badge/Sandstorm-kiwix-blue)](https://apps.sandstorm.io/app/5uh349d0kky2zp5whrh2znahn27gwha876xze3864n0fu9e5220h)
[![Build Status](https://github.com/kiwix/kiwix-tools/workflows/CI/badge.svg?query=branch%3Amain)](https://github.com/kiwix/kiwix-tools/actions?query=branch%3Amain)
[![Doc](https://readthedocs.org/projects/kiwix-tools/badge/?style=flat)](https://kiwix-tools.readthedocs.org/en/latest/?badge=latest)
[![CodeFactor](https://www.codefactor.io/repository/github/kiwix/kiwix-tools/badge)](https://www.codefactor.io/repository/github/kiwix/kiwix-tools)
[![License: GPL v3](https://img.shields.io/badge/License-GPLv3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0)
@@ -28,8 +29,8 @@ Preamble
--------
Although the Kiwix tools can be compiled/cross-compiled on/for many
systems, the following documentation explains how to do it on POSIX
ones. It is primarily thought for GNU/Linux systems and has been tested
sytems, the following documentation explains how to do it on POSIX
ones. It is primarly thought for GNU/Linux systems and has been tested
on recent releases of
[Debian](https://debian.org)/[Ubuntu](https://ubuntu.com) and
[Fedora](https://getfedora.org).
@@ -61,7 +62,7 @@ Environment
-------------
The Kiwix tools build using [Meson](http://mesonbuild.com/) version
0.43 or higher. Meson relies itself on Ninja, pkg-config and a few other
0.43 or higher. Meson relies itself on Ninja, pkg-config and few other
compilation tools. Install them first:
* [Meson](http://mesonbuild.com/)
* [Ninja](https://ninja-build.org/)
@@ -95,7 +96,7 @@ ninja -C build install
```
You might need to run the command as `root` (or using `sudo`),
depending on where you want to install the Kiwix tools. After the
depending where you want to install the Kiwix tools. After the
installation succeeded, you may need to run ldconfig (as `root`).
Uninstallation

10
debian/control vendored
View File

@@ -3,13 +3,11 @@ Section: utils
Priority: optional
Maintainer: Kiwix team <kiwix@kiwix.org>
Build-Depends: debhelper-compat (= 13),
libzim-dev (>= 9.0), libzim-dev (<< 10.0),
libkiwix-dev (>= 14.0), libkiwix-dev (<< 15.0),
cmake,
libdocopt-dev,
libkiwix-dev (>= 10.0.0~),
libzim-dev (>= 7.2.0~),
meson,
pkgconf,
Standards-Version: 4.6.2
pkg-config,
Standards-Version: 4.5.0
Homepage: https://github.com/kiwix/kiwix-tools
Rules-Requires-Root: no

View File

@@ -1,5 +1,5 @@
FROM alpine:3.22
LABEL org.opencontainers.image.source=https://github.com/openzim/kiwix-tools
FROM alpine:3.18
LABEL org.opencontainers.image.source https://github.com/openzim/kiwix-tools
# TARGETPLATFORM is injected by docker build
ARG TARGETPLATFORM

View File

@@ -2,18 +2,13 @@ ARG VERSION=latest
# kiwix-tools is multi-arch
FROM ghcr.io/kiwix/kiwix-tools:$VERSION
LABEL org.opencontainers.image.source=https://github.com/openzim/kiwix-tools
LABEL org.opencontainers.image.source https://github.com/openzim/kiwix-tools
# expose kiwix-serve default port and workdir
EXPOSE 8080
VOLUME /data
WORKDIR /data
# running as a named unprivileged user
RUN addgroup -S -g 1001 user && adduser -S -u 1001 user -G user
RUN chown user:user /data
USER user
COPY ./start.sh /usr/local/bin/
ENTRYPOINT ["/usr/bin/dumb-init", "--", "/usr/local/bin/start.sh"]

View File

@@ -42,12 +42,7 @@ Build an image for an ARM based GNU/Linux:
docker build . -t ghcr.io/kiwix/kiwix-serve:latest --build-arg ARCH="arm32v7/"
```
Docker Compose
--------------
You can also deploy kiwix with
[`docker-compose`](https://docs.docker.com/compose/). Check out a
sample at [docker-compose.yml.example](docker-compose.yml.example).
You can also deploy kiwix with [`docker-compose`](https://docs.docker.com/compose/). Check out a sample at [docker-compose.yml.example](docker-compose.yml.example)
Screenshots
-----------

View File

@@ -3,14 +3,6 @@
# Download if necessary a file
if [ ! -z "$DOWNLOAD" ]
then
# Check if /data is writable
if [ ! -w /data ]
then
echo "'/data' directory is not writable by '$(id -n -u):$(id -n -g)' ($(id -u):$(id -g)). ZIM file(s) can not be written."
exit 1
fi
# Dwonload ZIM file
ZIM=`basename $DOWNLOAD`
wget $DOWNLOAD -O "$ZIM"

View File

@@ -18,7 +18,7 @@ import os
# -- Project information -----------------------------------------------------
project = 'kiwix-tools'
copyright = '2024, kiwix-team'
copyright = '2022, kiwix-team'
author = 'kiwix-team'
@@ -30,7 +30,6 @@ on_rtd = os.environ.get('READTHEDOCS', None) == 'True'
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = [
"sphinx_rtd_theme"
]
# Add any paths that contain templates here, relative to this directory.
@@ -42,7 +41,8 @@ templates_path = ['_templates']
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
html_theme = 'sphinx_rtd_theme'
if not on_rtd:
html_theme = 'sphinx_rtd_theme'
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,

View File

@@ -17,7 +17,6 @@ file).
Clients can also remotely search inside those ZIM files that contain a full-text
search database.
``kiwix-serve`` supports Web browsers `Firefox >= 70, Chrome >= 80, Edge >= 80, ChromeAndroid >= 80, Safari >= 14, iOS >= 14 <https://browsersl.ist/#q=Firefox+%3E%3D+70%2C+Chrome+%3E%3D+80%2C+Edge+%3E%3D+80%2C+ChromeAndroid+%3E%3D+80%2C+Safari+%3E%3D+14%2C+iOS+%3E%3D+14>`_.
Usage
=====
@@ -49,32 +48,10 @@ Options
that the command line argument is rather a :ref:`library XML file
<cli-arg-library-file-path>`.
.. option:: --catalogOnly
In this mode ``kiwix-serve`` only serves the welcome (library) page and the
OPDS catalog. ZIM files referred by the :ref:`library XML file
<cli-arg-library-file-path>` need not be accessible.
This option may be combined with the :option:`--contentServerURL` option.
.. option:: --contentServerURL=URL
In :option:`--catalogOnly` mode book content is not served by this instance
of `kiwix-serve`. If a separate instance of `kiwix-serve` is running for the
same library without that option and thus serves book content, then the root
URL of that server can be passed to this instance so that books can still be
previewed.
This option must be combined with the :option:`--catalogOnly` option.
.. option:: -i ADDR, --address=ADDR
Listen only on this IP address. By default the server listens on all
available IP addresses. Alternatively, you can use special values to define which types of connections to accept:
- all : Listen for connections on all IP addresses (IPv4 and IPv6).
- ipv4 : Listen for connections on all IPv4 addresses.
- ipv6 : Listen for connections on all IPv6 addresses.
available IP addresses.
.. option:: -p PORT, --port=PORT
@@ -362,8 +339,8 @@ filtering criteria must be specified via the following URL parameters:
* ``q`` - include in the results only entries that contain the specified text
in the title or description.
* ``name`` - include in the results only entries with a matching
:term:`book name <Book name>`.
* ``name`` - include in the results only the entry with the specified
:term:`name <ZIM name>`.
Examples:
@@ -555,7 +532,7 @@ specified ZIM file.
**Parameters:**
``content``: :term:`name of the ZIM file <ZIM name>`.
``content``: :term:`name <ZIM name>` of the ZIM file.
.. _raw:
@@ -632,7 +609,7 @@ A multi-ZIM search request must comply with the following constraints:
below list take precedence over subsequent ones (the later ones, even if
present in the request, are simply ignored).
``content``: :term:`name of the ZIM file <ZIM name>` (for a single-ZIM
``content``: :term:`name <ZIM name>` of the ZIM file (for a single-ZIM
search). This is a :ref:`legacy parameter <deprecation>`. ``books.name``
should be used instead.
@@ -640,23 +617,9 @@ A multi-ZIM search request must comply with the following constraints:
a multi-ZIM search, however must respect the :ref:`multi-ZIM search
constraints <multi-zim-search-constraints>`.
.. note::
If any of the provided ``books.id`` values refers to a book missing
from the library then an error is returned instead of running the
search on the remaining (valid) entries.
``books.name``: :term:`name of the ZIM file <ZIM name>` (not to be confused
with ``books.filter.name`` which selects/filters based on the :term:`book
name <Book name>`). Can be repeated for a multi-ZIM search, however must
respect the :ref:`multi-ZIM search constraints
<multi-zim-search-constraints>`.
.. note::
If any of the provided ``books.name`` values refers to a book missing
from the library then an error is returned instead of running the
search on the remaining (valid) entries.
``books.name``: :term:`name <ZIM name>` of the ZIM file. Can be repeated
for a multi-ZIM search, however must respect the :ref:`multi-ZIM search
constraints <multi-zim-search-constraints>`.
``books.filter.{criteria}``: allows to take full advantage of :ref:`library
filtering <library-filtering>` functionality of the `/catalog/v2/entries`_
@@ -677,10 +640,10 @@ A multi-ZIM search request must comply with the following constraints:
``pageLength`` (optional, default: 25): maximum number of search results in
the response. Capped at 140.
``start`` (optional, default: 0): this parameter enables pagination of
``start`` (optional, default: 1): this parameter enables pagination of
results. The response will include up to ``pageLength`` results starting
with entry # ``start`` from the full list of search results (the first
result is assumed to have index 0).
result is assumed to have index 1).
Other parameters:
@@ -776,7 +739,7 @@ added as an option to perform a full text search in the said ZIM file.
**Parameters:**
``content`` (mandatory): :term:`name of the ZIM file <ZIM name>`.
``content`` (mandatory): :term:`name <ZIM name>` of the ZIM file.
``term`` (optional; defaults to an empty string): query text.
@@ -836,21 +799,6 @@ Glossary
.. glossary::
Book name
Name of the book as specified in the ZIM file metadata (for a
``kiwix-serve`` started *WITHOUT* the :option:`--library` option) or the
library XML file (for a ``kiwix-serve`` started with the
:option:`--library` option).
.. note::
Two or more books may have the same name in the library. That's not
considered a conflict, because there may be multiple versions of the
"same" book (differing by the settings of the scraper, date, etc).
:ref:`Library filtering <library-filtering>` by name will return all
matching books.
ZIM filename
Name of a ZIM file on the server filesystem.
@@ -860,22 +808,20 @@ Glossary
Identifier of a ZIM file in the server's library (used for referring to a
particular ZIM file in requests).
ZIM names are derived from the filenames as follows:
For a ``kiwix-serve`` started with a list of ZIM files, ZIM names are
derived from the filename by dropping the extension and replacing certain
characters (spaces are replaced with underscores, and ``+`` symbols are
replaced with the text ``plus``). Presence of the
:option:`-z`/:option:`--nodatealiases` option will create additional names
(aliases) for filenames with dates.
- file extension is removed,
- all characters are converted to lowercase,
- diacritics are removed,
- spaces are replaced with underscores,
- ``+`` symbols are replaced with the text ``plus``.
Presence of the :option:`-z`/:option:`--nodatealiases` option will create
additional names (aliases) for filenames with dates.
For a ``kiwix-serve`` started with the :option:`--library` option, ZIM
names come from the library XML file.
ZIM names are expected to be unique across the library. Any name conflicts
(including those caused by the usage of the
:option:`-z`/:option:`--nodatealiases` option) are reported on STDERR but,
otherwise, are ignored (i.e. only one of the entries can be accessed via
the conflicting name).
otherwise, are ignored.
ZIM title

View File

@@ -1,16 +1,12 @@
project('kiwix-tools', 'cpp',
version : '3.8.1',
version : '3.5.0',
license : 'GPL',
default_options: ['c_std=c11', 'cpp_std=c++17', 'werror=true'])
default_options: ['c_std=c11', 'cpp_std=c++11', 'werror=true'])
compiler = meson.get_compiler('cpp')
add_global_arguments('-DKIWIX_TOOLS_VERSION="@0@"'.format(meson.project_version()), language : 'cpp')
if host_machine.system() == 'windows'
add_project_arguments('-DNOMINMAX', language: 'cpp')
endif
static_linkage = get_option('static-linkage')
if static_linkage
# Static build is not supported on MacOS
@@ -20,11 +16,10 @@ if static_linkage
endif
thread_dep = dependency('threads')
libzim_dep = dependency('libzim', version:['>=9.0.0', '<10.0.0'], static:static_linkage)
libkiwix_dep = dependency('libkiwix', version:['>=14.1.0', '<15.0.0'], static:static_linkage)
libdocopt_dep = dependency('docopt', static:static_linkage)
kiwixlib_dep = dependency('kiwix', version:'>=12.0.0', static:static_linkage)
libzim_dep = dependency('libzim', version:'>=8.1.0', static:static_linkage)
all_deps = [thread_dep, libkiwix_dep, libzim_dep, libdocopt_dep]
all_deps = [thread_dep, kiwixlib_dep, libzim_dep]
if static_linkage
librt = compiler.find_library('rt', required:false)

View File

@@ -28,13 +28,7 @@ By default, kiwix-serve expects a list of ZIM files as command line arguments. P
.TP
\fB-i ADDR, --address=ADDR\fR
Listen only on this IP address. By default, the server listens on all available IP addresses. Alternatively, you can use special values to define which types of connections to accept:
all : Listen for connections on all IP addresses (IPv4 and IPv6).
.br
ipv4 : Listen for connections on all IPv4 addresses.
.br
ipv6 : Listen for connections on all IPv6 addresses.
Listen only on this IP address. By default, the server listens on all available IP addresses.
.TP
\fB-p PORT, --port=PORT\fR
@@ -94,10 +88,6 @@ Override the welcome page with a custom HTML file.
\fB-L N, --ipConnectionLimit=N\fR
Max number of (concurrent) connections per IP (default: infinite, recommended: >= 6).
.TP
\fB-k, --skipInvalid\fR
Startup even when ZIM files are invalid (those will be skipped)
.TP
\fB-v, --verbose\fR
Print debug log to STDOUT.

View File

@@ -17,7 +17,7 @@
* MA 02110-1301, USA.
*/
#include <docopt/docopt.h>
#include <getopt.h>
#include <kiwix/manager.h>
#include <kiwix/tools.h>
#include <cstdlib>
@@ -51,106 +51,159 @@ void show(const kiwix::Library& library, const std::string& bookId)
std::cout << std::endl;
}
// Older version of docopt doesn't declare Options. Let's declare it ourself.
using Options = std::map<std::string, docopt::value>;
/* Print correct console usage options */
static const char USAGE[] =
R"(Manipulates the Kiwix library XML file
void usage()
{
std::cout << "Usage:" << std::endl
<< "\tkiwix-manage LIBRARY_PATH add ZIM_PATH [OPTIONS]" << std::endl
<< "\tkiwix-manage LIBRARY_PATH remove ZIM_ID [ZIM_ID]..." << std::endl
<< "\tkiwix-manage LIBRARY_PATH show [ZIM_ID]..." << std::endl
<< std::endl
Usage:
kiwix-manage LIBRARYPATH add [--zimPathToSave=<custom_zim_path>] [--url=<http_zim_url>] ZIMPATH ...
kiwix-manage LIBRARYPATH (delete|remove) ZIMID ...
kiwix-manage LIBRARYPATH show [ZIMID ...]
kiwix-manage -v | --version
kiwix-manage -h | --help
<< "Purpose:" << std::endl
<< "\tManipulates the Kiwix library XML file"
<< std::endl << std::endl
Arguments:
LIBRARYPATH The XML library file path.
ZIMID ZIM file unique ID.
ZIMPATH A path to a ZIM to add.
<< "Arguments:" << std::endl
<< "\tLIBRARY_PATH\tis the XML library file path."
<< std::endl << std::endl
<< "\tACTION\t\tis the pre-defined string to specify the action to run on the XML library file."
<< std::endl << std::endl
<< "\t\t\tMust be one of the following values:" << std::endl
<< "\t\t\t* add: add a ZIM file to the library" << std::endl
<< "\t\t\t* remove: remove a ZIM file from the library" << std::endl
<< "\t\t\t* show: show the content of the library"
<< std::endl << std::endl
<< "\tZIM_ID\t\tZIM file unique ID"
<< std::endl << std::endl
<< "\tOPTIONS\t\tCustom options for \"add\" action:" << std::endl
<< "\t\t\t--zimPathToSave=CUSTOM_ZIM_PATH to replace the current ZIM file path" << std::endl
<< "\t\t\t--url=HTTP_ZIM_URL to create an \"url\" attribute for the online version of the ZIM file" << std::endl
<< std::endl
<< "\t\t\tOther options:" << std::endl
<< "\t\t\t-v, --version to print the software version" << std::endl
<< std::endl
Options:
Custom options for "add" action:
--zimPathToSave=<custom_zim_path> Replace the current ZIM file path
--url=<http_zim_url> Create an "url" attribute for the online version of the ZIM file
<< "Examples:" << std::endl
<< "\tAdd ZIM files to library: kiwix-manage my_library.xml add first.zim second.zim" << std::endl
<< "\tRemove ZIM files from library: kiwix-manage my_library.xml remove e5c2c003-b49e-2756-5176-5d9c86393dd9" << std::endl
<< "\tShow all library ZIM files: kiwix-manage my_library.xml show" << std::endl
<< std::endl
Other options:
-h --help Print this help
-v --version Print the software version
Examples:
Add ZIM files to library: kiwix-manage my_library.xml add first.zim second.zim
Remove ZIM files from library: kiwix-manage my_library.xml remove e5c2c003-b49e-2756-5176-5d9c86393dd9
Show all library ZIM files: kiwix-manage my_library.xml show
Documentation:
Source code https://github.com/kiwix/kiwix-tools
More info https://wiki.kiwix.org/wiki/kiwix-manage
)";
<< "Documentation:" << std::endl
<< "\tSource code\thttps://github.com/kiwix/kiwix-tools" << std::endl
<< "\tMore info\thttps://wiki.kiwix.org/wiki/Kiwix-manage" << std::endl
<< std::endl;
}
int handle_show(const kiwix::Library& library, const std::string& libraryPath,
const Options& options)
int argc, char* argv[])
{
if (options.at("ZIMID").asStringList().empty()) {
if (argc > 3 ) {
for(auto i=3; i<argc; i++) {
std::string bookId = argv[i];
show(library, bookId);
}
} else {
auto booksIds = library.getBooksIds();
for(auto& bookId: booksIds) {
show(library, bookId);
}
} else {
auto bookIds = options.at("ZIMID").asStringList();
for(auto& bookId: bookIds) {
show(library, bookId);
}
}
return(0);
}
int handle_add(kiwix::LibraryPtr library, const std::string& libraryPath,
const Options& options)
int handle_add(std::shared_ptr<kiwix::Library> library, const std::string& libraryPath,
int argc, char* argv[])
{
string zimPathToSave;
string zimPath;
string zimPathToSave = ".";
string url;
int option_index = 0;
int c = 0;
int resultCode = 0;
kiwix::Manager manager(library);
if (argc <= 3) {
std::cerr << "Path to zim file to add is missing in the command line" << std::endl;
return (-1);
}
auto zimPaths = options.at("ZIMPATH").asStringList();
for (auto& zimPath: zimPaths) {
if (options.at("--zimPathToSave").isString()) {
zimPathToSave = options.at("--zimPathToSave").asString();
} else {
zimPathToSave = zimPath;
}
if (options.at("--url").isString()) {
url = options.at("--url").asString();
}
/* Options parsing */
optind = 3;
static struct option long_options[] = {
{"url", required_argument, 0, 'u'},
{"zimPathToSave", required_argument, 0, 'z'},
{0, 0, 0, 0}
};
if (manager.addBookFromPathAndGetId(zimPath, zimPathToSave, url, false).empty()) {
std::cerr << "Cannot add ZIM " << zimPath << " to the library." << std::endl;
return 1;
bool has_option = false;
while (true) {
c = getopt_long(argc, argv, "cz:u:", long_options, &option_index);
if (c == -1)
break;
has_option = true;
switch (c) {
case 'u':
url = optarg;
break;
case 'z':
zimPathToSave = optarg;
break;
}
}
return 0;
if (optind >= argc) {
std::cerr << "Path to zim file to add is missing in the command line" << std::endl;
return (-1);
}
if (has_option && argc-optind > 1) {
std::cerr << "You cannot give option and several zim files to add" << std::endl;
return (-1);
}
kiwix::Manager manager(library);
for(auto i=optind; i<argc; i++) {
std::string zimPath = argv[i];
if (!zimPath.empty()) {
auto _zimPathToSave = zimPathToSave == "." ? zimPath : zimPathToSave;
if (manager.addBookFromPathAndGetId(zimPath, _zimPathToSave, url, false).empty()) {
std::cerr << "Cannot add zim " << zimPath << " to the library." << std::endl;
resultCode = 1;
}
} else {
std::cerr << "Invalid zim file path" << std::endl;
resultCode = 1;
}
}
return(resultCode);
}
int handle_remove(kiwix::Library& library, const std::string& libraryPath,
const Options& options)
int handle_remove(std::shared_ptr<kiwix::Library> library, const std::string& libraryPath,
int argc, char* argv[])
{
const unsigned int totalBookCount = library.getBookCount(true, true);
std::string bookId;
const unsigned int totalBookCount = library->getBookCount(true, true);
int exitCode = 0;
if (argc <= 3) {
std::cerr << "BookId to remove missing in the command line" << std::endl;
return (-1);
}
if (!totalBookCount) {
std::cerr << "Library is empty, no book to delete."
<< std::endl;
return 1;
}
auto bookIds = options.at("ZIMID").asStringList();
for (auto& bookId: bookIds) {
if (!library.removeBookById(bookId)) {
for (int i = 3; i<argc; i++) {
bookId = argv[i];
if (!library->removeBookById(bookId)) {
std::cerr << "Invalid book id '" << bookId << "'." << std::endl;
exitCode = 1;
}
@@ -161,37 +214,49 @@ int handle_remove(kiwix::Library& library, const std::string& libraryPath,
int main(int argc, char** argv)
{
string libraryPath = "";
supportedAction action = NONE;
auto library = kiwix::Library::create();
auto library = std::make_shared<kiwix::Library>();
Options args;
try {
args = docopt::docopt_parse(USAGE, {argv+1, argv+argc}, false, false);
} catch (docopt::DocoptArgumentError const & error ) {
std::cerr << error.what() << std::endl;
std::cerr << USAGE << std::endl;
/* General argument parsing */
static struct option long_options[] = {
{"version", no_argument, 0, 'v'},
{0, 0, 0, 0}
};
int option_index = 0;
int c;
while (true && argc == 2) {
c = getopt_long(argc, argv, "v", long_options, &option_index);
if (c == -1)
break;
switch (c) {
case 'v':
version();
return 0;
}
}
/* Action related argument parsing */
if (argc > 2) {
libraryPath = argv[1];
string actionString = argv[2];
if (actionString == "add")
action = ADD;
else if (actionString == "show")
action = SHOW;
else if (actionString == "remove" || actionString == "delete")
action = REMOVE;
}
/* Print usage)) if necessary */
if (libraryPath == "" || action == NONE) {
usage();
return -1;
}
if (args["--help"].asBool()) {
std::cout << USAGE << std::endl;
return 0;
}
if (args["--version"].asBool()) {
version();
return 0;
}
std::string libraryPath = args.at("LIBRARYPATH").asString();
if (args.at("add").asBool())
action = ADD;
else if (args.at("show").asBool())
action = SHOW;
else if (args.at("remove").asBool() || args.at("delete").asBool())
action = REMOVE;
/* Try to read the file */
libraryPath = kiwix::isRelativePath(libraryPath)
? kiwix::computeAbsolutePath(kiwix::getCurrentDirectory(), libraryPath)
@@ -208,13 +273,13 @@ int main(int argc, char** argv)
int exitCode = 0;
switch (action) {
case SHOW:
exitCode = handle_show(*library, libraryPath, args);
exitCode = handle_show(*library, libraryPath, argc, argv);
break;
case ADD:
exitCode = handle_add(library, libraryPath, args);
exitCode = handle_add(library, libraryPath, argc, argv);
break;
case REMOVE:
exitCode = handle_remove(*library, libraryPath, args);
exitCode = handle_remove(library, libraryPath, argc, argv);
break;
case NONE:
break;

View File

@@ -17,97 +17,99 @@
* MA 02110-1301, USA.
*/
#include <docopt/docopt.h>
#include <getopt.h>
#include <zim/search.h>
#include <zim/suggestion.h>
#include <kiwix/spelling_correction.h>
#include <xapian.h>
#include <iostream>
#include <filesystem>
#include "../version.h"
using namespace std;
// Older version of docopt doesn't declare Options. Let's declare it ourself.
using Options = std::map<std::string, docopt::value>;
static const char USAGE[] =
R"(Find articles based on a fulltext search pattern.
Usage:
kiwix-search [options] ZIM PATTERN
kiwix-search -h | --help
kiwix-search -V | --version
Arguments:
ZIM The full path of the ZIM file
PATTERN Word(s) - or part of - to search in the ZIM.
Options:
-s --suggestion Suggest article titles based on the few letters of the PATTERN instead of making a fulltext search. Work a bit like a completion solution
--spelling Suggest article titles based on the spelling corrected PATTERN instead of making a fulltext search.
-v --verbose Give details about the search process
-V --version Print software version
-h --help Print this help
)";
std::filesystem::path getKiwixCachedDataDirPath()
void usage()
{
std::filesystem::path home(getenv("HOME"));
std::filesystem::path cacheDirPath = home / ".cache" / "kiwix";
std::filesystem::create_directories(cacheDirPath);
return cacheDirPath;
cout << "Usage: kiwix-search [OPTIONS] ZIM PATTERN" << endl << endl
<< " kiwix-search allows one to find articles based on the a fulltext search pattern." << endl << endl
<< " ZIM is the full path of the ZIM file." << endl
<< " PATTERN is/are word(s) - or part of - to search in the ZIM." << endl << endl
<< " -s, --suggestion\tSuggest article titles based on the few letters of the PATTERN instead of making a fulltext search. Work a bit like a completion solution." << endl
<< " -v, --verbose\t\tGive details about the search process" << endl
<< " -V, --version\t\tPrint software version" << endl;
exit(1);
}
int main(int argc, char** argv)
{
Options args;
try {
args = docopt::docopt_parse(USAGE, {argv+1, argv+argc}, false, false);
} catch (docopt::DocoptArgumentError const & error ) {
std::cerr << error.what() << std::endl;
std::cerr << USAGE << std::endl;
return -1;
/* Init the variables */
// const char *indexPath =
// "/home/itamar/.www.kiwix.org/kiwix/43k0i1j4.default/6d2e587b-d586-dc6a-dc6a-e4ef035a1495d15c.index";
// const char *indexPath = "/home/itamar/testindex";
const char* zimPath = NULL;
const char* search = NULL;
bool verboseFlag = false;
bool suggestionFlag = false;
int option_index = 0;
int c = 0;
/* Argument parsing */
while (42) {
static struct option long_options[]
= {{"verbose", no_argument, 0, 'v'},
{"suggestion", no_argument, 0, 's'},
{"version", no_argument, 0, 'V'},
{0, 0, 0, 0}};
if (c != -1) {
c = getopt_long(argc, argv, "Vvsb:", long_options, &option_index);
switch (c) {
case 'v':
verboseFlag = true;
break;
case 'V':
version();
return 0;
case 's':
suggestionFlag = true;
break;
}
} else {
if (optind < argc) {
if (zimPath == NULL) {
zimPath = argv[optind++];
} else if (search == NULL) {
search = argv[optind++];
} else {
cout << zimPath << endl;
usage();
}
} else {
break;
}
}
}
if (args.at("--help").asBool()) {
std::cout << USAGE << std::endl;
return 0;
/* Check if we have enough arguments */
if (zimPath == NULL || search == NULL) {
usage();
}
if (args.at("--version").asBool()) {
version();
return 0;
}
auto zimPath = args.at("ZIM").asString();
auto pattern = args.at("PATTERN").asString();
auto verboseFlag = args.at("--verbose").asBool();
/* Try to prepare the indexing */
try {
zim::Archive archive(zimPath);
if (args.at("--suggestion").asBool()) {
if (suggestionFlag) {
zim::SuggestionSearcher searcher(archive);
searcher.setVerbose(verboseFlag);
for (const auto& r:searcher.suggest(pattern).getResults(0, 10)) {
for (const auto& r : searcher.suggest(search).getResults(0, 10) ) {
cout << r.getTitle() << endl;
}
} else if (args.at("--spelling").asBool()) {
kiwix::SpellingsDB spellingsDB(archive, getKiwixCachedDataDirPath());
for (const auto& r:spellingsDB.getSpellingCorrections(pattern, 1)) {
cout << r << endl;
}
} else {
zim::Searcher searcher(archive);
searcher.setVerbose(verboseFlag);
const zim::Query query(pattern);
const zim::Query query(search);
for (const auto& r : searcher.search(query).getResults(0, 10) ) {
cout << r.getTitle() << endl;
}
@@ -115,9 +117,6 @@ int main(int argc, char** argv)
} catch ( const std::runtime_error& err) {
cerr << err.what() << endl;
exit(1);
} catch ( const Xapian::Error& err) {
cerr << err.get_msg() << endl;
exit(1);
}
exit(0);

View File

@@ -18,7 +18,7 @@
* MA 02110-1301, USA.
*/
#include <docopt/docopt.h>
#include <getopt.h>
#include <kiwix/manager.h>
#include <kiwix/server.h>
#include <kiwix/name_mapper.h>
@@ -41,50 +41,49 @@
#include "../version.h"
#define DEFAULT_THREADS 4
#define LITERAL_AS_STR(A) #A
#define AS_STR(A) LITERAL_AS_STR(A)
void usage()
{
std::cout << "Usage:" << std::endl
<< "\tkiwix-serve [OPTIONS] ZIM_PATH+" << std::endl
<< "\tkiwix-serve --library [OPTIONS] LIBRARY_PATH" << std::endl
<< std::endl
static const char USAGE[] =
R"(Deliver ZIM file(s) articles via HTTP
<< "Purpose:" << std::endl
<< "\tDeliver ZIM file(s) articles via HTTP"
<< std::endl << std::endl
Usage:
kiwix-serve [options] ZIMPATH ...
kiwix-serve [options] (-l | --library) LIBRARYPATH
kiwix-serve -h | --help
kiwix-serve -V | --version
<< "Mandatory arguments:" << std::endl
<< "\tLIBRARY_PATH\t\tXML library file path listing ZIM file to serve. To be used only with the --library argument."
<< std::endl
<< "\tZIM_PATH\t\tZIM file path(s)"
<< std::endl << std::endl
Mandatory arguments:
LIBRARYPATH XML library file path listing ZIM file to serve. To be used only with the --library argument."
ZIMPATH ZIM file path(s)
<< "Optional arguments:" << std::endl << std::endl
<< "\t-h, --help\t\tPrint this help" << std::endl << std::endl
<< "\t-a, --attachToProcess\tExit if given process id is not running anymore" << std::endl
<< "\t-d, --daemon\t\tDetach the HTTP server daemon from the main process" << std::endl
<< "\t-i, --address\t\tListen only on this ip address, all available ones otherwise" << std::endl
<< "\t-M, --monitorLibrary\tMonitor the XML library file and reload it automatically" << std::endl
<< "\t-m, --nolibrarybutton\tDon't print the builtin home button in the builtin top bar overlay" << std::endl
<< "\t-n, --nosearchbar\tDon't print the builtin bar overlay on the top of each served page" << std::endl
<< "\t-b, --blockexternal\tPrevent users from directly accessing external links" << std::endl
<< "\t-p, --port\t\tTCP port on which to listen to HTTP requests (default: 80)" << std::endl
<< "\t-r, --urlRootLocation\tURL prefix on which the content should be made available (default: /)" << std::endl
<< "\t-s, --searchLimit\tMaximun number of zim in a fulltext multizim search (default: No limit)" << std::endl
<< "\t-t, --threads\t\tNumber of threads to run in parallel (default: " << DEFAULT_THREADS << ")" << std::endl
<< "\t-v, --verbose\t\tPrint debug log to STDOUT" << std::endl
<< "\t-V, --version\t\tPrint software version" << std::endl
<< "\t-z, --nodatealiases\tCreate URL aliases for each content by removing the date" << std::endl
<< "\t-c, --customIndex\tAdd path to custom index.html for welcome page" << std::endl
<< "\t-L, --ipConnectionLimit\tMax number of (concurrent) connections per IP (default: infinite, recommended: >= 6)" << std::endl
<< std::endl
Options:
-h --help Print this help
-a <pid> --attachToProcess=<pid> Exit if given process id is not running anymore [default: 0]
--catalogOnly Serve only the library catalog
--contentServerURL=<url> Root URL of the server serving ZIM content for this library
-d --daemon Detach the HTTP server daemon from the main process
-i <address> --address=<address> Listen only on the specified IP address. Specify 'ipv4', 'ipv6' or 'all' to listen on all IPv4, IPv6 or both types of addresses, respectively [default: all]
-M --monitorLibrary Monitor the XML library file and reload it automatically
-m --nolibrarybutton Don't print the builtin home button in the builtin top bar overlay
-n --nosearchbar Don't print the builtin bar overlay on the top of each served page
-b --blockexternal Prevent users from directly accessing external links
-p <port> --port=<port> Port on which to listen to HTTP requests [default: 80]
-r <root> --urlRootLocation=<root> URL prefix on which the content should be made available [default: /]
-s <limit> --searchLimit=<limit> Maximun number of zim in a fulltext multizim search [default: 0]
-t <threads> --threads=<threads> Number of threads to run in parallel [default: )" AS_STR(DEFAULT_THREADS) R"(]
-v --verbose Print debug log to STDOUT
-V --version Print software version
-z --nodatealiases Create URL aliases for each content by removing the date
-c <path> --customIndex=<path> Add path to custom index.html for welcome page
-L <limit> --ipConnectionLimit=<limit> Max number of (concurrent) connections per IP [default: 0] (recommended: >= 6)
-k --skipInvalid Startup even when ZIM files are invalid (those will be skipped)
Documentation:
Source code https://github.com/kiwix/kiwix-tools
More info https://wiki.kiwix.org/wiki/Kiwix-serve
https://kiwix-tools.readthedocs.io/en/latest/kiwix-serve.html
)";
<< "Documentation:" << std::endl
<< "\tSource code\t\thttps://github.com/kiwix/kiwix-tools" << std::endl
<< "\tMore info\t\thttps://wiki.kiwix.org/wiki/Kiwix-serve" << std::endl
<< std::endl;
}
std::string loadCustomTemplate (std::string customIndexPath) {
customIndexPath = kiwix::isRelativePath(customIndexPath) ?
@@ -104,9 +103,20 @@ std::string loadCustomTemplate (std::string customIndexPath) {
return indexTemplateString;
}
#ifndef _WIN32
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;
}
volatile sig_atomic_t waiting = false;
volatile sig_atomic_t libraryMustBeReloaded = false;
#ifndef _WIN32
void handle_sigterm(int signum)
{
if ( waiting == false ) {
@@ -136,9 +146,6 @@ void setup_sighandlers()
set_signal_handler(SIGINT, &handle_sigterm);
set_signal_handler(SIGHUP, &handle_sighup);
}
#else
bool waiting = false;
bool libraryMustBeReloaded = false;
#endif
uint64_t fileModificationTime(const std::string& path)
@@ -183,28 +190,6 @@ bool reloadLibrary(kiwix::Manager& mgr, const std::vector<std::string>& paths)
}
}
// docopt::value::isLong() is counting repeated values.
// It doesn't check if the string can be parsed as long.
// (Contrarly to `asLong` which will try to convert string to long)
// See https://github.com/docopt/docopt.cpp/issues/62
// `isLong` is a small helper to get if the value can be parsed as long.
inline bool isLong(const docopt::value& v) {
try {
v.asLong();
return true;
} catch (...) {
return false;
}
}
#define FLAG(NAME, VAR) if (arg.first == NAME) { VAR = arg.second.asBool(); continue; }
#define STRING(NAME, VAR) if (arg.first == NAME && arg.second.isString() ) { VAR = arg.second.asString(); continue; }
#define STRING_LIST(NAME, VAR, ERRORSTR) if (arg.first == NAME) { if (arg.second.isStringList()) { VAR = arg.second.asStringList(); continue; } else { errorString = ERRORSTR; break; } }
#define INT(NAME, VAR, ERRORSTR) if (arg.first == NAME ) { if (isLong(arg.second)) { VAR = arg.second.asLong(); continue; } else { errorString = ERRORSTR; break; } }
// Older version of docopt doesn't declare Options. Let's declare it ourself.
using Options = std::map<std::string, docopt::value>;
int main(int argc, char** argv)
{
#ifndef _WIN32
@@ -212,7 +197,7 @@ int main(int argc, char** argv)
#endif
std::string rootLocation = "/";
auto library = kiwix::Library::create();
auto library = std::make_shared<kiwix::Library>();
unsigned int nb_threads = DEFAULT_THREADS;
std::vector<std::string> zimPathes;
std::string libraryPath;
@@ -221,78 +206,134 @@ int main(int argc, char** argv)
std::string customIndexPath="";
std::string indexTemplateString="";
int serverPort = 80;
bool catalogOnlyFlag = false;
std::string contentServerURL;
bool daemonFlag [[gnu::unused]] = false;
bool helpFlag = false;
int daemonFlag [[gnu::unused]] = false;
int libraryFlag = false;
bool noLibraryButtonFlag = false;
bool noSearchBarFlag = false;
bool noDateAliasesFlag = false;
bool blockExternalLinks = false;
bool isVerboseFlag = false;
bool monitorLibrary = false;
bool versionFlag = false;
unsigned int PPID = 0;
int ipConnectionLimit = 0;
int searchLimit = 0;
bool skipInvalid = false;
std::string errorString;
static struct option long_options[]
= {{"daemon", no_argument, 0, 'd'},
{"help", no_argument, 0, 'h'},
{"verbose", no_argument, 0, 'v'},
{"version", no_argument, 0, 'V'},
{"library", no_argument, 0, 'l'},
{"nolibrarybutton", no_argument, 0, 'm'},
{"nodatealiases", no_argument, 0, 'z'},
{"nosearchbar", no_argument, 0, 'n'},
{"blockexternallinks", no_argument, 0, 'b'},
{"attachToProcess", required_argument, 0, 'a'},
{"port", required_argument, 0, 'p'},
{"address", required_argument, 0, 'i'},
{"threads", required_argument, 0, 't'},
{"urlRootLocation", required_argument, 0, 'r'},
{"customIndex", required_argument, 0, 'c'},
{"monitorLibrary", no_argument, 0, 'M'},
{"ipConnectionLimit", required_argument, 0, 'L'},
{"searchLimit", required_argument, 0, 's'},
{0, 0, 0, 0}};
Options args;
try {
args = docopt::docopt_parse(USAGE, {argv+1, argv+argc}, false, false);
} catch (docopt::DocoptArgumentError const & error) {
std::cerr << error.what() << std::endl;
std::cerr << USAGE << std::endl;
return -1;
std::set<int> usedOptions;
/* Argument parsing */
while (true) {
int option_index = 0;
int c
= getopt_long(argc, argv, "hzmnbdvVla:p:f:t:r:i:c:ML:s:", long_options, &option_index);
if (c != -1) {
auto insertRes = usedOptions.insert(c);
if (!insertRes.second) {
std::cerr << "Multiple values of same option are not allowed." << std::endl;
exit(1);
}
switch (c) {
case 'h':
usage();
return 0;
case 'd':
daemonFlag = true;
break;
case 'v':
isVerboseFlag = true;
break;
case 'V':
version();
return 0;
case 'l':
libraryFlag = true;
break;
case 'n':
noSearchBarFlag = true;
break;
case 'b':
blockExternalLinks = true;
break;
case 'z':
noDateAliasesFlag = true;
break;
case 'm':
noLibraryButtonFlag = true;
break;
case 'p':
serverPort = atoi(optarg);
break;
case 'a':
PPID = atoi(optarg);
break;
case 'i':
address = std::string(optarg);
break;
case 't':
nb_threads = atoi(optarg);
break;
case 'r':
rootLocation = std::string(optarg);
break;
case 'c':
customIndexPath = std::string(optarg);
break;
case 'M':
monitorLibrary = true;
break;
case 'L':
ipConnectionLimit = atoi(optarg);
break;
case 's':
searchLimit = atoi(optarg);
break;
case '?':
usage();
return 2;
}
} else {
if (optind < argc) {
if (libraryFlag) {
libraryPath = argv[optind++];
} else {
while (optind < argc)
zimPathes.push_back(std::string(argv[optind++]));
}
}
break;
}
}
for (auto const& arg: args) {
FLAG("--help", helpFlag)
FLAG("--catalogOnly", catalogOnlyFlag)
STRING("--contentServerURL", contentServerURL)
FLAG("--daemon", daemonFlag)
FLAG("--verbose", isVerboseFlag)
FLAG("--nosearchbar", noSearchBarFlag)
FLAG("--blockexternal", blockExternalLinks)
FLAG("--nodatealiases", noDateAliasesFlag)
FLAG("--nolibrarybutton",noLibraryButtonFlag)
FLAG("--monitorLibrary", monitorLibrary)
FLAG("--skipInvalid", skipInvalid)
FLAG("--version", versionFlag)
STRING("LIBRARYPATH", libraryPath)
INT("--port", serverPort, "Port must be an integer")
INT("--attachToProcess", PPID, "Process to attach must be an integer")
STRING("--address", address)
INT("--threads", nb_threads, "Number of threads must be an integer")
STRING("--urlRootLocation", rootLocation)
STRING("--customIndex", customIndexPath)
INT("--ipConnectionLimit", ipConnectionLimit, "IP connection limit must be an integer")
INT("--searchLimit", searchLimit, "Search limit must be an integer")
STRING_LIST("ZIMPATH", zimPathes, "ZIMPATH must be a string list")
}
if (!errorString.empty()) {
std::cerr << errorString << std::endl;
std::cerr << USAGE << std::endl;
return -1;
}
if (helpFlag) {
std::cout << USAGE << std::endl;
return 0;
}
if (versionFlag) {
version();
return 0;
}
/* Print usage)) if necessary */
if (zimPathes.empty() && libraryPath.empty()) {
usage();
exit(1);
}
/* Setup the library manager and get the list of books */
kiwix::Manager manager(library);
std::vector<std::string> libraryPaths;
if (!libraryPath.empty()) {
if (libraryFlag) {
libraryPaths = kiwix::split(libraryPath, ";");
if ( !reloadLibrary(manager, libraryPaths) ) {
exit(1);
@@ -307,32 +348,15 @@ int main(int argc, char** argv)
std::vector<std::string>::iterator it;
for (it = zimPathes.begin(); it != zimPathes.end(); it++) {
if (!manager.addBookFromPath(*it, *it, "", false)) {
if (skipInvalid) {
std::cerr << "Skipping invalid '" << *it << "' ...continuing" << std::endl;
} else {
std::cerr << "Unable to add the ZIM file '" << *it
<< "' to the internal library." << std::endl;
exit(1);
}
std::cerr << "Unable to add the ZIM file '" << *it
<< "' to the internal library." << std::endl;
exit(1);
}
}
}
auto libraryFileTimestamp = newestFileTimestamp(libraryPaths);
auto curLibraryFileTimestamp = libraryFileTimestamp;
kiwix::IpMode ipMode = kiwix::IpMode::AUTO;
if (address == "all") {
address.clear();
ipMode = kiwix::IpMode::ALL;
} else if (address == "ipv4") {
address.clear();
ipMode = kiwix::IpMode::IPV4;
} else if (address == "ipv6") {
address.clear();
ipMode = kiwix::IpMode::IPV6;
}
#ifndef _WIN32
/* Fork if necessary */
if (daemonFlag) {
@@ -353,7 +377,7 @@ int main(int argc, char** argv)
#endif
auto nameMapper = std::make_shared<kiwix::UpdatableNameMapper>(library, noDateAliasesFlag);
kiwix::Server server(library, nameMapper);
kiwix::Server::Configuration configuration(library, nameMapper);
if (!customIndexPath.empty()) {
try {
@@ -364,30 +388,25 @@ int main(int argc, char** argv)
}
}
server.setAddress(address);
server.setRoot(rootLocation);
server.setPort(serverPort);
server.setNbThreads(nb_threads);
server.setVerbose(isVerboseFlag);
server.setTaskbar(!noSearchBarFlag, !noLibraryButtonFlag);
server.setBlockExternalLinks(blockExternalLinks);
server.setIndexTemplateString(indexTemplateString);
server.setIpConnectionLimit(ipConnectionLimit);
server.setMultiZimSearchLimit(searchLimit);
server.setIpMode(ipMode);
server.setCatalogOnlyMode(catalogOnlyFlag);
while ( !contentServerURL.empty() && contentServerURL.back() == '/' )
contentServerURL.pop_back();
server.setContentServerUrl(contentServerURL);
configuration.setAddress(address);
configuration.setRoot(rootLocation);
configuration.setPort(serverPort);
configuration.setNbThreads(nb_threads);
configuration.setVerbose(isVerboseFlag);
configuration.setTaskbar(!noSearchBarFlag, !noLibraryButtonFlag);
configuration.setBlockExternalLinks(blockExternalLinks);
configuration.setIndexTemplateString(indexTemplateString);
configuration.setIpConnectionLimit(ipConnectionLimit);
configuration.setMultiZimSearchLimit(searchLimit);
kiwix::Server server(configuration);
if (! server.start()) {
exit(1);
}
std::cout << "The Kiwix server is running and can be accessed in the local network at: " << std::endl;
for (const auto& url : server.getServerAccessUrls()) {
std::cout << " - " << url << std::endl;
}
std::string url = "http://" + server.getAddress() + ":" + std::to_string(server.getPort()) + normalizeRootUrl(rootLocation);
std::cout << "The Kiwix server is running and can be accessed in the local network at: "
<< url << std::endl;
/* Run endless (until PPID dies) */
waiting = true;
@@ -423,9 +442,7 @@ int main(int argc, char** argv)
if ( monitorLibrary ) {
curLibraryFileTimestamp = newestFileTimestamp(libraryPaths);
if ( !libraryMustBeReloaded ) {
libraryMustBeReloaded = curLibraryFileTimestamp > libraryFileTimestamp;
}
libraryMustBeReloaded += curLibraryFileTimestamp > libraryFileTimestamp;
}
if ( libraryMustBeReloaded && !libraryPaths.empty() ) {