Repository: paperless-ngx/paperless-ngx

Issue:
[BUG] Unknown argument X-GM-LABELS error while fetching mails via IMAP
### Description

The issue from https://github.com/paperless-ngx/paperless-ngx/issues/1753 seems to occur again in 1.14.2

### Steps to reproduce

1. Configure a mail account with a non-gmail mailserver (f.e. dovecot)
2. Create a rule with action "Tag" (5)
3. Let paperless try to fetch mails

### Webserver logs

```bash
[2023-04-28 19:00:00,318] [DEBUG] [paperless_mail] Processing mail account xxx
[2023-04-28 19:00:00,490] [DEBUG] [paperless_mail] GMAIL Label Support: False
[2023-04-28 19:00:00,492] [DEBUG] [paperless_mail] AUTH=PLAIN Support: True
[2023-04-28 19:00:00,530] [DEBUG] [paperless_mail] Account xxx: Processing 2 rule(s)
[2023-04-28 19:00:00,540] [DEBUG] [paperless_mail] Rule xxx: Selecting folder INBOX
[2023-04-28 19:00:00,553] [DEBUG] [paperless_mail] Rule xxx: Searching folder with criteria ((NOT (X-GM-LABELS "paperless") UNKEYWORD paperless) SINCE 14-Apr-2023)
[2023-04-28 19:00:00,565] [ERROR] [paperless_mail] Rule xxx: Error while processing rule: SEARCH command error: BAD [b'Error in IMAP command SEARCH: Unknown argument X-GM-LABELS (0.000 + 0.000 secs).']
Traceback (most recent call last):
  File "/usr/src/paperless/src/paperless_mail/mail.py", line 490, in handle_mail_account
    total_processed_files += self._handle_mail_rule(
  File "/usr/src/paperless/src/paperless_mail/mail.py", line 561, in _handle_mail_rule
    for message in messages:
  File "/usr/local/lib/python3.9/site-packages/imap_tools/mailbox.py", line 170, in fetch
    nums = tuple((reversed if reverse else iter)(self.numbers(criteria, charset)))[limit_range]
  File "/usr/local/lib/python3.9/site-packages/imap_tools/mailbox.py", line 108, in numbers
    search_result = self.client.search(charset, encoded_criteria)
  File "/usr/local/lib/python3.9/imaplib.py", line 732, in search
    typ, dat = self._simple_command(name, 'CHARSET', charset, *criteria)
  File "/usr/local/lib/python3.9/imaplib.py", line 1230, in _simple_command
    return self._command_complete(name, self._command(name, *args))
  File "/usr/local/lib/python3.9/imaplib.py", line 1055, in _command_complete
    raise self.error('%s command error: %s %s' % (name, typ, data))
imaplib.IMAP4.error: SEARCH command error: BAD [b'Error in IMAP command SEARCH: Unknown argument X-GM-LABELS (0.000 + 0.000 secs).']
```


### Browser logs

_No response_

### Paperless-ngx version

1.14.2

### Host OS

Debian

### Installation method

Docker - official image

### Browser

_No response_

### Configuration changes

_No response_

### Other

_No response_


Discussion:
Most servers must ignore the unknown argument, but not all.  Likely easy to fix, it's possible to know if a server supports the Gmail extensions

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
@@ -2,7 +2,6 @@
 import itertools
 import logging
 import os
-import re
 import tempfile
 import traceback
 from datetime import date
@@ -145,7 +144,7 @@ class TagMailAction(BaseMailAction):
     A mail action that tags mails after processing.
     """
 
-    def __init__(self, parameter):
+    def __init__(self, parameter: str, supports_gmail_labels: bool):
         # The custom tag should look like "apple:<color>"
         if "apple:" in parameter.lower():
             _, self.color = parameter.split(":")
@@ -159,18 +158,22 @@ def __init__(self, parameter):
         else:
             self.keyword = parameter
             self.color = None
+        self.supports_gmail_labels = supports_gmail_labels
 
     def get_criteria(self):
         # AppleMail: We only need to check if mails are \Flagged
         if self.color:
             return {"flagged": False}
         elif self.keyword:
-            return AND(NOT(gmail_label=self.keyword), no_keyword=self.keyword)
+            if self.supports_gmail_labels:
+                return AND(NOT(gmail_label=self.keyword), no_keyword=self.keyword)
+            else:
+                return NOT(no_keyword=self.keyword)
         else:  # pragma: nocover
             raise ValueError("This should never happen.")
 
     def post_consume(self, M: MailBox, message_uid: str, parameter: str):
-        if re.search(r"gmail\.com$|googlemail\.com$", M._host):
+        if self.supports_gmail_labels:
             M.client.uid("STORE", message_uid, "+X-GM-LABELS", self.keyword)
 
         # AppleMail
@@ -248,15 +251,18 @@ def apply_mail_action(
         message_date = make_aware(message_date)
 
     try:
-        action = get_rule_action(rule)
-
         with get_mailbox(
             server=account.imap_server,
             port=account.imap_port,
             security=account.imap_security,
         ) as M:
+            # Need to know the support for the possible tagging
+            supports_gmail_labels = "X-GM-EXT-1" in M.client.capabilities
+
             mailbox_login(M, account)
             M.folder.set(rule.folder)
+
+            action = get_rule_action(rule, supports_gmail_labels)
             action.post_consume(M, message_uid, rule.action_parameter)
 
         ProcessedMail.objects.create(
@@ -337,7 +343,7 @@ def queue_consumption_tasks(
     ).delay()
 
 
-def get_rule_action(rule) -> BaseMailAction:
+def get_rule_action(rule: MailRule, supports_gmail_labels: bool) -> BaseMailAction:
     """
     Returns a BaseMailAction instance for the given rule.
     """
@@ -351,12 +357,12 @@ def get_rule_action(rule) -> BaseMailAction:
     elif rule.action == MailRule.MailAction.MARK_READ:
         return MarkReadMailAction()
     elif rule.action == MailRule.MailAction.TAG:
-        return TagMailAction(rule.action_parameter)
+        return TagMailAction(rule.action_parameter, supports_gmail_labels)
     else:
         raise NotImplementedError("Unknown action.")  # pragma: nocover
 
 
-def make_criterias(rule):
+def make_criterias(rule: MailRule, supports_gmail_labels: bool):
     """
     Returns criteria to be applied to MailBox.fetch for the given rule.
     """
@@ -374,7 +380,7 @@ def make_criterias(rule):
     if rule.filter_body:
         criterias["body"] = rule.filter_body
 
-    rule_query = get_rule_action(rule).get_criteria()
+    rule_query = get_rule_action(rule, supports_gmail_labels).get_criteria()
     if isinstance(rule_query, dict):
         if len(rule_query) or len(criterias):
             return AND(**rule_query, **criterias)
@@ -490,6 +496,7 @@ def handle_mail_account(self, account: MailAccount):
                         total_processed_files += self._handle_mail_rule(
                             M,
                             rule,
+                            supports_gmail_labels,
                         )
                     except Exception as e:
                         self.log(
@@ -512,6 +519,7 @@ def _handle_mail_rule(
         self,
         M: MailBox,
         rule: MailRule,
+        supports_gmail_labels: bool,
     ):
         self.log("debug", f"Rule {rule}: Selecting folder {rule.folder}")
 
@@ -537,7 +545,7 @@ def _handle_mail_rule(
                 f"does not exist in account {rule.account}",
             ) from err
 
-        criterias = make_criterias(rule)
+        criterias = make_criterias(rule, supports_gmail_labels)
 
         self.log(
             "debug",


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
@@ -737,6 +737,7 @@ def test_tag_mail_action_applemail_wrong_input(self):
             MailError,
             TagMailAction,
             "apple:black",
+            False,
         )
 
     def test_handle_mail_account_tag_applemail(self):