mirror of
				https://github.com/paperless-ngx/paperless-ngx.git
				synced 2025-11-03 03:16:10 -06:00 
			
		
		
		
	Change: use simpler method for attaching files (#8845)
This commit is contained in:
		@@ -1,7 +1,5 @@
 | 
				
			|||||||
from email.encoders import encode_base64
 | 
					from email import message_from_bytes
 | 
				
			||||||
from email.mime.base import MIMEBase
 | 
					 | 
				
			||||||
from pathlib import Path
 | 
					from pathlib import Path
 | 
				
			||||||
from urllib.parse import quote
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
from django.conf import settings
 | 
					from django.conf import settings
 | 
				
			||||||
from django.core.mail import EmailMessage
 | 
					from django.core.mail import EmailMessage
 | 
				
			||||||
@@ -27,35 +25,14 @@ def send_email(
 | 
				
			|||||||
    if attachment:
 | 
					    if attachment:
 | 
				
			||||||
        # Something could be renaming the file concurrently so it can't be attached
 | 
					        # Something could be renaming the file concurrently so it can't be attached
 | 
				
			||||||
        with FileLock(settings.MEDIA_LOCK), attachment.open("rb") as f:
 | 
					        with FileLock(settings.MEDIA_LOCK), attachment.open("rb") as f:
 | 
				
			||||||
            file_content = f.read()
 | 
					            content = f.read()
 | 
				
			||||||
 | 
					            if attachment_mime_type == "message/rfc822":
 | 
				
			||||||
 | 
					                # See https://forum.djangoproject.com/t/using-emailmessage-with-an-attached-email-file-crashes-due-to-non-ascii/37981
 | 
				
			||||||
 | 
					                content = message_from_bytes(f.read())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            main_type, sub_type = (
 | 
					            email.attach(
 | 
				
			||||||
                attachment_mime_type.split("/", 1)
 | 
					                filename=attachment.name,
 | 
				
			||||||
                if attachment_mime_type
 | 
					                content=content,
 | 
				
			||||||
                else ("application", "octet-stream")
 | 
					                mimetype=attachment_mime_type,
 | 
				
			||||||
            )
 | 
					            )
 | 
				
			||||||
            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()
 | 
					    return email.send()
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										63
									
								
								src/documents/tests/samples/eml_with_umlaut.eml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										63
									
								
								src/documents/tests/samples/eml_with_umlaut.eml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,63 @@
 | 
				
			|||||||
 | 
					From: =?UTF-8?Q?My_Name=C3=B6er?= <myaddr@volkswagen.de>
 | 
				
			||||||
 | 
					Return-Path: <myaddr@volkswagen.de>
 | 
				
			||||||
 | 
					X-Original-To: rechnung@domain.de
 | 
				
			||||||
 | 
					Delivered-To: rechnung@domain.de
 | 
				
			||||||
 | 
					DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=domainb.de; s=default;
 | 
				
			||||||
 | 
						t=1736973836; bh=bCUrrHd7c5mrvMbK20=;
 | 
				
			||||||
 | 
						h=Date:To:From:Subject:From;
 | 
				
			||||||
 | 
						b=QPaQKuzx2adfCr0S18KVgA5x01KXZknaaEpQW49Ock2ghScLAvv3ij8xfzUbZewCT
 | 
				
			||||||
 | 
						 CuUAYBmCxbN5ygIztJXfgWpl1Cx5FsVQNpdZ/6Ns=
 | 
				
			||||||
 | 
					Received: by mail.domain.de (Postfix, from userid 121)
 | 
				
			||||||
 | 
						id 407BCE078A; Wed, 15 Jan 2025 21:43:56 +0100 (CET)
 | 
				
			||||||
 | 
					X-Spam-Checker-Version: SpamAssassin 4.0.0 (2022-12-13) on imail.domain.de
 | 
				
			||||||
 | 
					X-Spam-Level:
 | 
				
			||||||
 | 
					X-Spam-Status: No, score=-3.0 required=1.7 tests=ALL_TRUSTED,BAYES_00,
 | 
				
			||||||
 | 
						DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU autolearn=ham autolearn_force=no
 | 
				
			||||||
 | 
						version=4.0.0
 | 
				
			||||||
 | 
					DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=domain.de; s=default;
 | 
				
			||||||
 | 
						t=1736973835; bh=bCUrrHvn+Hd7c5mrvMbK20=;
 | 
				
			||||||
 | 
						h=Date:To:From:Subject:From;
 | 
				
			||||||
 | 
						b=AjGxzFALRR0AixC1uRhFuQkb4MoBqju1NInlUzx9w+toniNx3ifgkXpGxiV7+JJsr
 | 
				
			||||||
 | 
						 Z+jNZxck3D3M05ETYnrGInO+vDlosfFU2WqnZn+E=
 | 
				
			||||||
 | 
					Received: from [192.168.8.154] (unknown [1.1.1.1])
 | 
				
			||||||
 | 
						(using TLSv1.3 with cipher TLS_AES_128_GCM_SHA256 (128/128 bits)
 | 
				
			||||||
 | 
						 key-exchange X25519 server-signature ECDSA (prime256v1) server-digest
 | 
				
			||||||
 | 
					 SHA256)
 | 
				
			||||||
 | 
						(No client certificate requested)
 | 
				
			||||||
 | 
						(Authenticated sender: myuser)
 | 
				
			||||||
 | 
						by mail.domain.de (Postfix) with ESMTPSA id C8BC6DF926
 | 
				
			||||||
 | 
						for <rechnung@domain.de>; Wed, 15 Jan 2025 21:43:55 +0100 (CET)
 | 
				
			||||||
 | 
					Message-ID: <da0c12c1-58c3-4f3d-ab89-9ae04@domain.de>
 | 
				
			||||||
 | 
					Date: Wed, 15 Jan 2025 21:43:52 +0100
 | 
				
			||||||
 | 
					MIME-Version: 1.0
 | 
				
			||||||
 | 
					User-Agent: Mozilla Thunderbird
 | 
				
			||||||
 | 
					Content-Language: de-DE
 | 
				
			||||||
 | 
					To: rechnung@domain.de
 | 
				
			||||||
 | 
					Subject: No Umlauts here
 | 
				
			||||||
 | 
					Autocrypt: addr=myaddr@domain.de; keydata=
 | 
				
			||||||
 | 
					 xsDiBEK4/dERBACj7Kn2Skjnyq/Q69FKLSd9WJg/7Ta3aZwWiaizzAnB/avBoN9/NPkVCQbB
 | 
				
			||||||
 | 
					 jeJ8G/uOtYDCgjmxeBNMVM3DOMTu4QfLnl0BoQz811bxiaPqQ6YLRA4MZrawZwerIOS2oSk2
 | 
				
			||||||
 | 
					 FDGKsZvAYCG439QK102XPlSPC7c4/oQ+3fwkeqFpEwCg4XYOfTNzis6CZPgkQqyVrpaYR5kD
 | 
				
			||||||
 | 
					 /j1HIDd1B75eeCb8ifoyWoWHB+cVHR+kEuMw1FMZt7UQ6Pb5nfQTcpEvrH9BTc0GKmTzj1N3
 | 
				
			||||||
 | 
					 ExOPaNaGtsc7FAST+5dYflfL1+WVzsNJWgIp6PoAL1XoCZ6l63/qOrHtnp6l42IO8Rg2lDcc
 | 
				
			||||||
 | 
					 25YdfiRSlTWuKvleT/okyc6jHioEA/9bUPbpdmUyR5kWRkdRBTjjCipl+o8rSlparnnk+7jh
 | 
				
			||||||
 | 
					 1cvOHJlNJ/MYP9vcgDGYFIv+38sY4+UuBBoNmSS7yN5yKpT+XIsSgMEvyRPP6lr1GJ76aT2v
 | 
				
			||||||
 | 
					 dIvcozHdC9g+nu6AlKgywdWW3hq5IjqRqnmVQfUN/1dL/D1ZImclEJoZR80lQ2hyaXN0aWFu
 | 
				
			||||||
 | 
					 IFZvZWxrZXIgPGN2b2Vsa2VyQGtuZWJiLmRlPsJ3BBMRCAA3AhsjBgsJCAcDAgQVAggDBBYC
 | 
				
			||||||
 | 
					 AwECHgECF4AWIQQl96acg1HmEUgEtrfRc0hiUBebOwUCXwQmvwAKCRDRc0hiUBebOxeiAJ43
 | 
				
			||||||
 | 
					 bk2DCMuEVho3wRUqEyhNk0/mwQCaA60n1eTn+6bs2WXttTVGkBJGadzOwU0EQrj92RAIAKJz
 | 
				
			||||||
 | 
					 rvwheohL4D327LEpy1AkIjUJotYUt9fPW+MVDSsoyj67HFTRz1WcK51+/8Fi6jedKxmR3hAi
 | 
				
			||||||
 | 
					 GlZRvpsJ2chOuaynMac0Uv42rnSGHcLZf0KxLG+r7HOPSEAnSrbDAhWbuqyV994vCIfG9LDz
 | 
				
			||||||
 | 
					 RDocaUEyJ7M+QV4VGS6Z3PPgxm78kCJ5TGHXRA96ponSptkyfIxvKHBa2TyrhMoLj4TmW4CO
 | 
				
			||||||
 | 
					 SHmQD2e3EVIYlhERdPEQ5DmCljeO19ZopjNOLcAx4eOyguwvjpdeLUQJdaryWo56USWKbrmU
 | 
				
			||||||
 | 
					 VrK4OodWkgcUvaagvey0MkABZkY0RMRKrfMuGb+Vw2nH9OGaRysAAwYH/AxC/+/m+OTA6tmA
 | 
				
			||||||
 | 
					 AXd31vpMNUdVoPjyO+FQ7f8mwXa3SjPZeQLvpA1RfYFdDtSfr16RI8s41xtL12IYZr4nyRG/
 | 
				
			||||||
 | 
					 wYPmM2WvcTUp3vWVizzHSERlarONc7aaCGXghg6Trpbz7+tv2MOpRLMfJd+6kyCz5pRSGeuX
 | 
				
			||||||
 | 
					 z0iIxWSny1+Vc9uGgxyjJ21FFuvYPR8xmjfCGXvsnWLhKxTPNdhIG6/im/1/uTznzlfGUvgx
 | 
				
			||||||
 | 
					 eNuzVphaVSPzP5DBVxJbKZzZYKOydQLx0Z79YF2xCGmz80EsSajpQNMvNYuNQXuH1ogFIP7e
 | 
				
			||||||
 | 
					 PNOoaoakYuLE1YMhWL+AJzYRRevW8k/VLBgsYvbCRgQYEQIABgUCQrj92QAKCRDRc0hiUBeb
 | 
				
			||||||
 | 
					 O/HXAJ0WAbB0sQ0SBVF+2Nlabw4HICAiKwCg4Fe9VjcfR4+ZJqq3Mx1c+IAE65c=
 | 
				
			||||||
 | 
					Content-Type: text/plain; charset=UTF-8; format=flowed
 | 
				
			||||||
 | 
					Content-Transfer-Encoding: 8bit
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					But here: üöäüäö
 | 
				
			||||||
@@ -2149,9 +2149,8 @@ class TestWorkflows(
 | 
				
			|||||||
        EMAIL_ENABLED=True,
 | 
					        EMAIL_ENABLED=True,
 | 
				
			||||||
        PAPERLESS_URL="http://localhost:8000",
 | 
					        PAPERLESS_URL="http://localhost:8000",
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
    @mock.patch("httpx.post")
 | 
					 | 
				
			||||||
    @mock.patch("django.core.mail.message.EmailMessage.send")
 | 
					    @mock.patch("django.core.mail.message.EmailMessage.send")
 | 
				
			||||||
    def test_workflow_email_include_file(self, mock_email_send, mock_post):
 | 
					    def test_workflow_email_include_file(self, mock_email_send):
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        GIVEN:
 | 
					        GIVEN:
 | 
				
			||||||
            - Document updated workflow with email action
 | 
					            - Document updated workflow with email action
 | 
				
			||||||
@@ -2199,6 +2198,24 @@ class TestWorkflows(
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        mock_email_send.assert_called_once()
 | 
					        mock_email_send.assert_called_once()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        mock_email_send.reset_mock()
 | 
				
			||||||
 | 
					        # test with .eml file
 | 
				
			||||||
 | 
					        test_file2 = shutil.copy(
 | 
				
			||||||
 | 
					            self.SAMPLE_DIR / "eml_with_umlaut.eml",
 | 
				
			||||||
 | 
					            self.dirs.scratch_dir / "eml_with_umlaut.eml",
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        doc2 = Document.objects.create(
 | 
				
			||||||
 | 
					            title="sample eml",
 | 
				
			||||||
 | 
					            checksum="123456",
 | 
				
			||||||
 | 
					            filename=test_file2,
 | 
				
			||||||
 | 
					            mime_type="message/rfc822",
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        run_workflows(WorkflowTrigger.WorkflowTriggerType.DOCUMENT_UPDATED, doc2)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        mock_email_send.assert_called_once()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @override_settings(
 | 
					    @override_settings(
 | 
				
			||||||
        EMAIL_ENABLED=False,
 | 
					        EMAIL_ENABLED=False,
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user