148 lines
4.9 KiB
Python
148 lines
4.9 KiB
Python
from __future__ import annotations
|
|
|
|
from pathlib import Path
|
|
|
|
from django import forms
|
|
from django.utils.translation import gettext as _
|
|
|
|
|
|
def _header_matches(extension: str, header: bytes) -> bool:
|
|
extension = extension.lower().lstrip(".")
|
|
if extension == "png":
|
|
return header.startswith(b"\x89PNG\r\n\x1a\n")
|
|
if extension in {"jpg", "jpeg"}:
|
|
return header.startswith(b"\xff\xd8\xff")
|
|
if extension == "webp":
|
|
return header.startswith(b"RIFF") and header[8:12] == b"WEBP"
|
|
if extension == "pdf":
|
|
return header.startswith(b"%PDF")
|
|
if extension == "ico":
|
|
return header.startswith(b"\x00\x00\x01\x00")
|
|
if extension == "svg":
|
|
text = header.decode("utf-8", errors="ignore").lower()
|
|
return "<svg" in text
|
|
return False
|
|
|
|
|
|
def validate_uploaded_file(
|
|
uploaded_file,
|
|
*,
|
|
allowed_extensions: set[str],
|
|
max_size_bytes: int,
|
|
allowed_content_types: set[str] | None = None,
|
|
invalid_type_message: str,
|
|
size_message: str,
|
|
unreadable_message: str,
|
|
) -> None:
|
|
if not uploaded_file:
|
|
return
|
|
|
|
if getattr(uploaded_file, "size", 0) > max_size_bytes:
|
|
raise forms.ValidationError(size_message)
|
|
|
|
extension = Path(getattr(uploaded_file, "name", "")).suffix.lower().lstrip(".")
|
|
if extension not in allowed_extensions:
|
|
raise forms.ValidationError(invalid_type_message)
|
|
|
|
content_type = (getattr(uploaded_file, "content_type", "") or "").lower().strip()
|
|
if allowed_content_types and content_type and content_type not in allowed_content_types:
|
|
raise forms.ValidationError(invalid_type_message)
|
|
|
|
try:
|
|
header = uploaded_file.read(512)
|
|
uploaded_file.seek(0)
|
|
except Exception as exc:
|
|
raise forms.ValidationError(unreadable_message) from exc
|
|
|
|
if not _header_matches(extension, header):
|
|
raise forms.ValidationError(invalid_type_message)
|
|
|
|
|
|
def validate_avatar_upload(uploaded_file) -> None:
|
|
validate_uploaded_file(
|
|
uploaded_file,
|
|
allowed_extensions={"png", "jpg", "jpeg", "webp", "svg"},
|
|
max_size_bytes=5 * 1024 * 1024,
|
|
allowed_content_types={
|
|
"image/png",
|
|
"image/x-png",
|
|
"image/jpeg",
|
|
"image/jpg",
|
|
"image/pjpeg",
|
|
"image/webp",
|
|
"image/svg+xml",
|
|
},
|
|
invalid_type_message=_("Bitte ein PNG-, JPG-, WEBP- oder SVG-Bild hochladen."),
|
|
size_message=_("Das Profilbild darf maximal 5 MB groß sein."),
|
|
unreadable_message=_("Die Bilddatei konnte nicht gelesen werden."),
|
|
)
|
|
|
|
|
|
def validate_logo_upload(uploaded_file) -> None:
|
|
validate_uploaded_file(
|
|
uploaded_file,
|
|
allowed_extensions={"svg", "png", "jpg", "jpeg", "webp"},
|
|
max_size_bytes=5 * 1024 * 1024,
|
|
allowed_content_types={
|
|
"image/png",
|
|
"image/x-png",
|
|
"image/jpeg",
|
|
"image/jpg",
|
|
"image/pjpeg",
|
|
"image/webp",
|
|
"image/svg+xml",
|
|
},
|
|
invalid_type_message=_("Bitte ein SVG-, PNG-, JPG- oder WEBP-Bild hochladen."),
|
|
size_message=_("Das Logo darf maximal 5 MB groß sein."),
|
|
unreadable_message=_("Die Logo-Datei konnte nicht gelesen werden."),
|
|
)
|
|
|
|
|
|
def validate_favicon_upload(uploaded_file) -> None:
|
|
validate_uploaded_file(
|
|
uploaded_file,
|
|
allowed_extensions={"ico", "png", "svg", "webp"},
|
|
max_size_bytes=2 * 1024 * 1024,
|
|
allowed_content_types={
|
|
"image/x-icon",
|
|
"image/vnd.microsoft.icon",
|
|
"image/png",
|
|
"image/x-png",
|
|
"image/webp",
|
|
"image/svg+xml",
|
|
},
|
|
invalid_type_message=_("Bitte eine ICO-, PNG-, SVG- oder WEBP-Datei hochladen."),
|
|
size_message=_("Das Favicon darf maximal 2 MB groß sein."),
|
|
unreadable_message=_("Die Favicon-Datei konnte nicht gelesen werden."),
|
|
)
|
|
|
|
|
|
def validate_pdf_upload(uploaded_file) -> None:
|
|
validate_uploaded_file(
|
|
uploaded_file,
|
|
allowed_extensions={"pdf"},
|
|
max_size_bytes=10 * 1024 * 1024,
|
|
allowed_content_types={"application/pdf"},
|
|
invalid_type_message=_("Bitte eine gültige PDF-Datei hochladen."),
|
|
size_message=_("Der PDF-Briefkopf darf maximal 10 MB groß sein."),
|
|
unreadable_message=_("Die PDF-Datei konnte nicht gelesen werden."),
|
|
)
|
|
|
|
|
|
def validate_signature_upload(uploaded_file) -> None:
|
|
validate_uploaded_file(
|
|
uploaded_file,
|
|
allowed_extensions={"png", "jpg", "jpeg"},
|
|
max_size_bytes=4 * 1024 * 1024,
|
|
allowed_content_types={
|
|
"image/png",
|
|
"image/x-png",
|
|
"image/jpeg",
|
|
"image/jpg",
|
|
"image/pjpeg",
|
|
},
|
|
invalid_type_message=_("Bitte eine PNG- oder JPG-Datei hochladen."),
|
|
size_message=_("Die Signatur-Datei ist zu groß (max. 4 MB)."),
|
|
unreadable_message=_("Die Signatur-Datei konnte nicht gelesen werden."),
|
|
)
|