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 " 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."), )