Repository: paperless-ngx/paperless-ngx

Issue:
[BUG] Mail rule filter attachment filename is case sensitive
### Description

The "Mail rules" interface says the Filter attachment filename should be case insensitive:

> Only consume documents which entirely match this filename if specified. Wildcards such as *.pdf or \*invoice\* are allowed. Case insensitive.

The latest revision of mail.py uses fnmatch, which follows the operating system's rules for case sensitivity:

https://github.com/paperless-ngx/paperless-ngx/blob/a728502988fe26da9aa2844916dcfcc2455d0be2/src/paperless_mail/mail.py#L286-L288

Issue ported from https://github.com/jonaswinkler/paperless-ng/issues/1394

### Expected behavior

Mail attachments should be matched case insensitive to the Filter attachment filename.

### Steps to reproduce

1. Set up paperless to consume from a mailbox
2. Set up a mail rule with a filter attachment filename of *.pdf
3. Send an attachment ending in *.PDF

### Webserver logs

_No response_

### Screenshots

_No response_

### Paperless-ngx version

1.6.0

### Host OS

Oracle Linux 8

### Installation method

Docker

### Browser

_No response_

### Configuration changes

_No response_

### Other

_No response_


Discussion:
I can confirm this with *.pdf / *.PDF

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
@@ -253,7 +253,7 @@ def handle_mail_rule(self, M, rule):
 
         return total_processed_files
 
-    def handle_message(self, message, rule):
+    def handle_message(self, message, rule) -> int:
         if not message.attachments:
             return 0
 
@@ -285,7 +285,12 @@ def handle_message(self, message, rule):
                 continue
 
             if rule.filter_attachment_filename:
-                if not fnmatch(att.filename, rule.filter_attachment_filename):
+                # Force the filename and pattern to the lowercase
+                # as this is system dependent otherwise
+                if not fnmatch(
+                    att.filename.lower(),
+                    rule.filter_attachment_filename.lower(),
+                ):
                     continue
 
             title = self.get_title(message, att, rule)


Test patch:
diff --git a/src/paperless_mail/tests/test_mail.py b/src/paperless_mail/tests/test_mail.py
--- a/src/paperless_mail/tests/test_mail.py
+++ b/src/paperless_mail/tests/test_mail.py
@@ -409,18 +409,21 @@ def test_filename_filter(self):
                 _AttachmentDef(filename="f2.pdf"),
                 _AttachmentDef(filename="f3.pdf"),
                 _AttachmentDef(filename="f2.png"),
+                _AttachmentDef(filename="file.PDf"),
+                _AttachmentDef(filename="f1.Pdf"),
             ],
         )
 
         tests = [
-            ("*.pdf", ["f1.pdf", "f2.pdf", "f3.pdf"]),
-            ("f1.pdf", ["f1.pdf"]),
+            ("*.pdf", ["f1.pdf", "f1.Pdf", "f2.pdf", "f3.pdf", "file.PDf"]),
+            ("f1.pdf", ["f1.pdf", "f1.Pdf"]),
             ("f1", []),
-            ("*", ["f1.pdf", "f2.pdf", "f3.pdf", "f2.png"]),
+            ("*", ["f1.pdf", "f2.pdf", "f3.pdf", "f2.png", "f1.Pdf", "file.PDf"]),
             ("*.png", ["f2.png"]),
         ]
 
         for (pattern, matches) in tests:
+            matches.sort()
             self.async_task.reset_mock()
             account = MailAccount()
             rule = MailRule(
@@ -431,11 +434,11 @@ def test_filename_filter(self):
 
             result = self.mail_account_handler.handle_message(message, rule)
 
-            self.assertEqual(result, len(matches))
-            filenames = [
-                a[1]["override_filename"] for a in self.async_task.call_args_list
-            ]
-            self.assertCountEqual(filenames, matches)
+            self.assertEqual(result, len(matches), f"Error with pattern: {pattern}")
+            filenames = sorted(
+                [a[1]["override_filename"] for a in self.async_task.call_args_list],
+            )
+            self.assertListEqual(filenames, matches)
 
     def test_handle_mail_account_mark_read(self):