snapshot of https://github.com/zulip/zulip.git @ 48c69911c7dd4e03b20552eb3bd77ad8151c1357 (2026-09-15)
Some checks failed
Code scanning / CodeQL (push) Failing after 15s
Zulip production suite / Ubuntu 22.04 production build (push) Failing after 26s
Zulip production suite / Debian 12 production install with custom db name and user (push) Has been skipped
Zulip production suite / Ubuntu 22.04 production install and PostgreSQL upgrade with pgroonga (push) Has been skipped
Zulip production suite / Ubuntu 24.04 production install (push) Has been skipped
Zulip production suite / Ubuntu 26.04 production install (push) Has been skipped
Zulip production suite / Debian 13 production install (push) Has been skipped
Zulip production suite / 7.0 Version Upgrade (push) Has been skipped
Zulip production suite / 8.0 Version Upgrade (push) Has been skipped
Zulip production suite / 6.0 Version Upgrade (push) Has been skipped
Zulip production suite / 10.0 Version Upgrade (push) Has been skipped
Zulip production suite / 9.0 Version Upgrade (push) Has been skipped
Zulip production suite / 12.0 Version Upgrade (push) Has been skipped
Zulip production suite / 11.0 Version Upgrade (push) Has been skipped
GitHub Actions Security Analysis with zizmor / zizmor (push) Failing after 3s
Zulip CI / Debian 12 (Python 3.11, backend + documentation) (push) Failing after 26s
Zulip CI / Ubuntu 22.04 (Python 3.10, backend + frontend) (push) Failing after 3s
Zulip CI / Ubuntu 24.04 (Python 3.12, backend) (push) Failing after 25s
Zulip CI / Ubuntu 26.04 (Python 3.14, backend) (push) Failing after 25s
Zulip CI / Debian 13 (Python 3.13, backend) (push) Failing after 28s
Zulip production suite / Required jobs (push) Has been cancelled
Zulip CI / Required jobs (push) Has been cancelled
API Documentation Update Check / check-feature-level-updated (push) Has been cancelled
API Documentation Update Check / notify-if-api-docs-changed (push) Has been cancelled
Some checks failed
Code scanning / CodeQL (push) Failing after 15s
Zulip production suite / Ubuntu 22.04 production build (push) Failing after 26s
Zulip production suite / Debian 12 production install with custom db name and user (push) Has been skipped
Zulip production suite / Ubuntu 22.04 production install and PostgreSQL upgrade with pgroonga (push) Has been skipped
Zulip production suite / Ubuntu 24.04 production install (push) Has been skipped
Zulip production suite / Ubuntu 26.04 production install (push) Has been skipped
Zulip production suite / Debian 13 production install (push) Has been skipped
Zulip production suite / 7.0 Version Upgrade (push) Has been skipped
Zulip production suite / 8.0 Version Upgrade (push) Has been skipped
Zulip production suite / 6.0 Version Upgrade (push) Has been skipped
Zulip production suite / 10.0 Version Upgrade (push) Has been skipped
Zulip production suite / 9.0 Version Upgrade (push) Has been skipped
Zulip production suite / 12.0 Version Upgrade (push) Has been skipped
Zulip production suite / 11.0 Version Upgrade (push) Has been skipped
GitHub Actions Security Analysis with zizmor / zizmor (push) Failing after 3s
Zulip CI / Debian 12 (Python 3.11, backend + documentation) (push) Failing after 26s
Zulip CI / Ubuntu 22.04 (Python 3.10, backend + frontend) (push) Failing after 3s
Zulip CI / Ubuntu 24.04 (Python 3.12, backend) (push) Failing after 25s
Zulip CI / Ubuntu 26.04 (Python 3.14, backend) (push) Failing after 25s
Zulip CI / Debian 13 (Python 3.13, backend) (push) Failing after 28s
Zulip production suite / Required jobs (push) Has been cancelled
Zulip CI / Required jobs (push) Has been cancelled
API Documentation Update Check / check-feature-level-updated (push) Has been cancelled
API Documentation Update Check / notify-if-api-docs-changed (push) Has been cancelled
This commit is contained in:
60
zerver/upload_handler.py
Normal file
60
zerver/upload_handler.py
Normal file
@@ -0,0 +1,60 @@
|
||||
import os
|
||||
import tempfile
|
||||
|
||||
from django.core.files.uploadhandler import TemporaryFileUploadHandler
|
||||
from typing_extensions import override
|
||||
|
||||
# Django spools each uploaded file to a temporary file named
|
||||
# f"{tempfile.gettempprefix()}{8 random characters}.upload{extension}",
|
||||
# where the extension is taken verbatim from the client-provided filename.
|
||||
#
|
||||
# Since a filename is limited to NAME_MAX (255) bytes, a sufficiently
|
||||
# long extension makes creating that temporary file fail with
|
||||
# 'OSError: [Errno 36] File name too long', which Django does not handle.
|
||||
TEMPORARY_FILE_MAX_EXTENSION_LENGTH = 255 - len(tempfile.gettempprefix()) - 8 - len(".upload")
|
||||
|
||||
|
||||
def truncate_filename_extension(filename: str) -> str:
|
||||
"""Shortens filename extensions that are too long to fit in the name of
|
||||
the temporary file Django spools an upload to. NAME_MAX limits bytes
|
||||
rather than characters, so we truncate the encoded extension and discard
|
||||
any character left partially encoded at the end.
|
||||
"""
|
||||
root, extension = os.path.splitext(filename)
|
||||
encoded_extension = extension.encode()
|
||||
if len(encoded_extension) <= TEMPORARY_FILE_MAX_EXTENSION_LENGTH:
|
||||
return filename
|
||||
return root + encoded_extension[:TEMPORARY_FILE_MAX_EXTENSION_LENGTH].decode(errors="ignore")
|
||||
|
||||
|
||||
class ZulipTemporaryFileUploadHandler(TemporaryFileUploadHandler):
|
||||
"""Django's TemporaryFileUploadHandler, made tolerant of filenames whose
|
||||
extension alone exceeds what the filesystem allows in a filename.
|
||||
|
||||
Django does sanitize an over-long filename, in UploadedFile._set_name,
|
||||
but only after creating the temporary file whose name embeds the
|
||||
extension; shortening the extension is therefore something we have to do
|
||||
beforehand. The cap is far longer than any extension in practice.
|
||||
|
||||
This workaround can go away once https://code.djangoproject.com/ticket/37292
|
||||
is fixed upstream.
|
||||
"""
|
||||
|
||||
@override
|
||||
def new_file(
|
||||
self,
|
||||
field_name: str,
|
||||
file_name: str,
|
||||
content_type: str,
|
||||
content_length: int | None,
|
||||
charset: str | None = None,
|
||||
content_type_extra: dict[str, bytes] | None = None,
|
||||
) -> None:
|
||||
super().new_file(
|
||||
field_name,
|
||||
truncate_filename_extension(file_name),
|
||||
content_type,
|
||||
content_length,
|
||||
charset,
|
||||
content_type_extra,
|
||||
)
|
||||
Reference in New Issue
Block a user