Fix: use MIMEBase for email attachments (#8762)

This commit is contained in:
shamoon 2025-01-16 10:48:19 -08:00 committed by GitHub
parent 283bcb4c91
commit a32077566b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 65 additions and 8 deletions

61
src/documents/mail.py Normal file
View File

@ -0,0 +1,61 @@
from email.encoders import encode_base64
from email.mime.base import MIMEBase
from pathlib import Path
from urllib.parse import quote
from django.conf import settings
from django.core.mail import EmailMessage
from filelock import FileLock
def send_email(
subject: str,
body: str,
to: list[str],
attachment: Path | None = None,
attachment_mime_type: str | None = None,
) -> int:
"""
Send an email with an optional attachment.
TODO: re-evaluate this pending https://code.djangoproject.com/ticket/35581 / https://github.com/django/django/pull/18966
"""
email = EmailMessage(
subject=subject,
body=body,
to=to,
)
if attachment:
# Something could be renaming the file concurrently so it can't be attached
with FileLock(settings.MEDIA_LOCK), attachment.open("rb") as f:
file_content = f.read()
main_type, sub_type = (
attachment_mime_type.split("/", 1)
if attachment_mime_type
else ("application", "octet-stream")
)
mime_part = MIMEBase(main_type, sub_type)
mime_part.set_payload(file_content)
encode_base64(mime_part)
# see https://github.com/stumpylog/tika-client/blob/f65a2b792fc3cf15b9b119501bba9bddfac15fcc/src/tika_client/_base.py#L46-L57
try:
attachment.name.encode("ascii")
except UnicodeEncodeError:
filename_safed = attachment.name.encode("ascii", "ignore").decode(
"ascii",
)
filepath_quoted = quote(attachment.name, encoding="utf-8")
mime_part.add_header(
"Content-Disposition",
f"attachment; filename={filename_safed}; filename*=UTF-8''{filepath_quoted}",
)
else:
mime_part.add_header(
"Content-Disposition",
f"attachment; filename={attachment.name}",
)
email.attach(mime_part)
return email.send()

View File

@ -12,7 +12,6 @@ from celery.signals import task_postrun
from celery.signals import task_prerun
from django.conf import settings
from django.contrib.auth.models import User
from django.core.mail import EmailMessage
from django.db import DatabaseError
from django.db import close_old_connections
from django.db import models
@ -30,6 +29,7 @@ from documents.data_models import DocumentMetadataOverrides
from documents.file_handling import create_source_path_directory
from documents.file_handling import delete_empty_directories
from documents.file_handling import generate_unique_filename
from documents.mail import send_email
from documents.models import Correspondent
from documents.models import CustomField
from documents.models import CustomFieldInstance
@ -972,17 +972,13 @@ def run_workflows(
doc_url,
)
try:
email = EmailMessage(
n_messages = send_email(
subject=subject,
body=body,
to=action.email.to.split(","),
attachment=original_file if action.email.include_document else None,
attachment_mime_type=document.mime_type,
)
if action.email.include_document:
# Something could be renaming the file concurrently so it can't be attached
with FileLock(settings.MEDIA_LOCK):
document.refresh_from_db()
email.attach_file(original_file)
n_messages = email.send()
logger.debug(
f"Sent {n_messages} notification email(s) to {action.email.to}",
extra={"group": logging_group},