Repository: paperless-ngx/paperless-ngx

Issue:
[BUG] Processing some mail runs in to error
### Description

Some mail processing runs into an error and the mail attachments are not imported. It could have to do with the change from #4931 because I have not had problems at the beginning of december.

### Steps to reproduce

1. Account on web.de
2. Activate the Deutsche Post Briefankündigung
3. Setup E-Mail processing
4. Get a Mail send per E-Post to you containing a digital copy of the letter as PDF and an image (png) of the envelop.

### Webserver logs

```bash
[2023-12-23 03:40:01,814] [ERROR] [paperless_mail] Rule Web.de.ePost Digitale Kopie: Error while processing mail 159: [Errno 21] Is a directory: '/tmp/paperless/paperless-mail-9ixp8o7a'
Traceback (most recent call last):
  File "/usr/src/paperless/src/paperless_mail/mail.py", line 591, in _handle_mail_rule
    processed_files = self._handle_message(message, rule)
                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/src/paperless/src/paperless_mail/mail.py", line 639, in _handle_message
    processed_elements += self._process_attachments(
                          ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/src/paperless/src/paperless_mail/mail.py", line 715, in _process_attachments
    temp_filename.write_bytes(att.payload)
  File "/usr/local/lib/python3.11/pathlib.py", line 1067, in write_bytes
    with self.open(mode='wb') as f:
         ^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/pathlib.py", line 1044, in open
    return io.open(self, mode, buffering, encoding, errors, newline)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
IsADirectoryError: [Errno 21] Is a directory: '/tmp/paperless/paperless-mail-9ixp8o7a'
```


### Browser logs

_No response_

### Paperless-ngx version

2.1.3

### Host OS

Ubuntu 22.04

### Installation method

Docker - official image

### Browser

_No response_

### Configuration changes

_No response_

### Other

_No response_

### Please confirm the following

- [X] I believe this issue is a bug that affects all users of Paperless-ngx, not something specific to my installation.
- [X] I have already searched for relevant existing issues and discussions before opening this report.
- [X] I have updated the title field above with a concise description.


Discussion:
I cant reproduce this, and Im not aware of anything having changed or others reporting this. Does this happen with other emails or other mail accounts? I assume you cant post the email but are there any odd attachment files (empty tracking pixels etc?)

```
[2023-12-22 21:02:37,761] [DEBUG] [paperless_mail] Rule Test rule w tags: Selecting folder INBOX
[2023-12-22 21:02:37,817] [DEBUG] [paperless_mail] Rule Test rule w tags: Searching folder with criteria (SINCE 21-Dec-2023 UNKEYWORD imported-dev SUBJECT "[paperless-dev]")
[2023-12-22 21:02:38,427] [DEBUG] [paperless_mail] Rule Test rule w tags: Processing mail [paperless-dev] Test v2.1.3 from _ with 1 attachment(s)
[2023-12-22 21:02:38,435] [INFO] [paperless_mail] Rule Test rule w tags: Consuming attachment zmeo-23-1538925.pdf from mail [paperless-dev] Test v2.1.3 from _
[2023-12-22 21:02:38,505] [DEBUG] [paperless_mail] Rule Test rule w tags: Processed 1 matching mail(s)
...
[2023-12-22 21:02:38,547] [INFO] [paperless.consumer] Consuming zmeo-23-1538925.pdf
[2023-12-22 21:02:38,549] [DEBUG] [paperless.consumer] Detected mime type: application/pdf
[2023-12-22 21:02:38,550] [DEBUG] [paperless.consumer] Parser: RasterisedDocumentParser
[2023-12-22 21:02:38,552] [DEBUG] [paperless.consumer] Parsing zmeo-23-1538925.pdf...
...
[2023-12-22 21:02:41,902] [DEBUG] [paperless.consumer] Creation date from parse_date: 2018-08-11 00:00:00-07:00
[2023-12-22 21:02:42,410] [DEBUG] [paperless.consumer] Deleting file /tmp/paperless/paperless-ngxs41i_gwa/zmeo-23-1538925.pdf
[2023-12-22 21:02:42,421] [DEBUG] [paperless.parsing.tesseract] Deleting directory /tmp/paperless/paperless-gtyslo_h
```
It's changed a tiny bit, to keep the original attachment filename, but the flow is pretty simple:

1. Get a temporary directory
2. Construct the file inside the directory using the attachment filename (sanitized)
3. Write the attachment payload to the file.

I would assume step 2 is actually the problem, the filename is probably something not really valid, such as `.` or `..`(being the current directory or parent directory).  If you can't share the email, can you download it and look for lines like `Content-Disposition: attachment; filename="600+kbfile.txt"`?
Setting the attachment type from „Process all files, including 'inline' attachments“ to „Only process attachments“ imports the pdf and ignores the png with the mail envelop which is an inline attachment. So the problem seem to be with inline attachments. I will further investigate after my holidays at the beginning of next year.
Possible that inline attachments don't actually have filenames.  I'll have to look after the holidays

Resolving patch:
diff --git a/src/paperless_mail/mail.py b/src/paperless_mail/mail.py
--- a/src/paperless_mail/mail.py
+++ b/src/paperless_mail/mail.py
@@ -703,6 +703,12 @@ def _process_attachments(
             mime_type = magic.from_buffer(att.payload, mime=True)
 
             if is_mime_type_supported(mime_type):
+                self.log.info(
+                    f"Rule {rule}: "
+                    f"Consuming attachment {att.filename} from mail "
+                    f"{message.subject} from {message.from_}",
+                )
+
                 os.makedirs(settings.SCRATCH_DIR, exist_ok=True)
 
                 temp_dir = Path(
@@ -711,14 +717,15 @@ def _process_attachments(
                         dir=settings.SCRATCH_DIR,
                     ),
                 )
-                temp_filename = temp_dir / pathvalidate.sanitize_filename(att.filename)
-                temp_filename.write_bytes(att.payload)
 
-                self.log.info(
-                    f"Rule {rule}: "
-                    f"Consuming attachment {att.filename} from mail "
-                    f"{message.subject} from {message.from_}",
-                )
+                attachment_name = pathvalidate.sanitize_filename(att.filename)
+                if attachment_name:
+                    temp_filename = temp_dir / attachment_name
+                else:  # pragma: no cover
+                    # Some cases may have no name (generally inline)
+                    temp_filename = temp_dir / "no-name-attachment"
+
+                temp_filename.write_bytes(att.payload)
 
                 input_doc = ConsumableDocument(
                     source=DocumentSource.MailFetch,


Test patch: