FreedomBox/plinth/modules/kiwix/privileged.py
Sunil Mohan Adapa 80e6d940a4
*: Use action_utils.run instead of subprocess.check_call
- This is to capture stdout and stderr and transmit that from privileged daemon
back to the service to be displayed in HTML.

Tests:

- Unit tests and code checks pass.

- Some of the modified actions work as expected.

- systemd daemon-reload is performed during infinoted setup.

Signed-off-by: Sunil Mohan Adapa <sunil@medhas.org>
Reviewed-by: Veiko Aasa <veiko17@disroot.org>
2025-09-29 16:58:54 +03:00

108 lines
3.4 KiB
Python

# SPDX-License-Identifier: AGPL-3.0-or-later
"""
Privileged actions for Kiwix content server.
"""
import pathlib
import shutil
import subprocess
from xml.etree import ElementTree
from plinth import action_utils
from plinth.actions import privileged
from plinth.modules import kiwix
# Only one central library is supported.
KIWIX_HOME = pathlib.Path('/var/lib/kiwix-server-freedombox')
LIBRARY_FILE = KIWIX_HOME / 'library_zim.xml'
CONTENT_DIR = KIWIX_HOME / 'content'
@privileged
def add_package(file_name: str, temporary_file_path: str):
"""Adds a content package to Kiwix.
Adding packages is idempotent.
Users can add content to Kiwix in multiple ways:
- Upload a ZIM file
- Provide a link to the ZIM file
- Provide a magnet link to the ZIM file
The commandline download manager aria2c is a dependency of kiwix-tools.
aria2c is used for both HTTP and Magnet downloads.
"""
kiwix.validate_file_name(file_name)
# file_name is verified further by move_uploaded_file()
# Moving files to the Kiwix library path ensures that
# they can't be removed by other apps or users.
CONTENT_DIR.mkdir(exist_ok=True)
action_utils.move_uploaded_file(temporary_file_path, CONTENT_DIR,
file_name, allow_overwrite=False,
user='root', group='root',
permissions=0o644)
content_file = CONTENT_DIR / file_name
try:
_kiwix_manage_add(str(CONTENT_DIR / file_name))
except subprocess.CalledProcessError:
# Remove the uploaded file if we could not added it to library
content_file.unlink()
raise
def _kiwix_manage_add(zim_file: str):
action_utils.run(['kiwix-manage', LIBRARY_FILE, 'add', zim_file],
check=True)
# kiwix-serve doesn't read the library file unless it is restarted.
action_utils.service_try_restart('kiwix-server-freedombox')
@privileged
def uninstall() -> None:
"""Remove all content during uninstall."""
shutil.rmtree(str(CONTENT_DIR), ignore_errors=True)
LIBRARY_FILE.unlink(missing_ok=True)
@privileged
def list_packages() -> dict[str, dict[str, str]]:
"""Return the list of content packages configured in library file."""
library = ElementTree.parse(LIBRARY_FILE).getroot()
books = {}
for book in library:
path = book.attrib['path'].split('/')[-1]
path = path.removesuffix('.zim').lower() # Strip '.zim' from the path
try:
books[book.attrib['id']] = {
'title': book.attrib['title'],
'description': book.attrib['description'],
'path': path
}
except KeyError:
pass # Ignore entries that don't have expected properties
return books
@privileged
def delete_package(zim_id: str):
"""Remove a content package from the library file."""
library = ElementTree.parse(LIBRARY_FILE).getroot()
for book in library:
try:
if book.attrib['id'] != zim_id:
continue
action_utils.run(['kiwix-manage', LIBRARY_FILE, 'remove', zim_id],
check=True)
(KIWIX_HOME / book.attrib['path']).unlink()
action_utils.service_try_restart('kiwix-server-freedombox')
return
except KeyError: # Expected properties not found on elements
pass