FreedomBox/plinth/modules/kiwix/privileged.py
Joseph Nuthalapati 6dd6f12f5a
kiwix: Use new utility for handling uploads
Earlier, the uploaded ZIM file was being written to disk twice.

Manual Test
-----------
Without the changes in this commit, the English MediaWiki archive of
6.83 GB cannot be uploaded to the dev container of size 12 GB, since two
temporary files are created.

With the changes in this commit, the same file can be uploaded
successfully and accessed using Kiwix reader.

- Uploaded file has expected ownership and permissions.

Signed-off-by: Joseph Nuthalapati <njoseph@riseup.net>
[sunil: Handle error for uploading duplicate content.]
[sunil: Set root:root ownership on the uploaded file.]
[sunil: Use the action utility for checking that the upload file and moving it.]
Signed-off-by: Sunil Mohan Adapa <sunil@medhas.org>
Reviewed-by: Veiko Aasa <veiko17@disroot.org>
2024-10-03 11:45:15 +03:00

100 lines
3.1 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)
# 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)
_kiwix_manage_add(str(CONTENT_DIR / file_name))
def _kiwix_manage_add(zim_file: str):
subprocess.check_call(['kiwix-manage', LIBRARY_FILE, 'add', zim_file])
# 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
subprocess.check_call(
['kiwix-manage', LIBRARY_FILE, 'remove', zim_id])
(KIWIX_HOME / book.attrib['path']).unlink()
action_utils.service_try_restart('kiwix-server-freedombox')
return
except KeyError: # Expected properties not found on elements
pass