Merge branch 'dev' into fix-mail-starttls

This commit is contained in:
phail
2022-04-13 23:55:38 +02:00
128 changed files with 11610 additions and 6203 deletions

View File

@@ -62,13 +62,13 @@ class FlagMailAction(BaseMailAction):
def get_rule_action(rule):
if rule.action == MailRule.ACTION_FLAG:
if rule.action == MailRule.AttachmentAction.FLAG:
return FlagMailAction()
elif rule.action == MailRule.ACTION_DELETE:
elif rule.action == MailRule.AttachmentAction.DELETE:
return DeleteMailAction()
elif rule.action == MailRule.ACTION_MOVE:
elif rule.action == MailRule.AttachmentAction.MOVE:
return MoveMailAction()
elif rule.action == MailRule.ACTION_MARK_READ:
elif rule.action == MailRule.AttachmentAction.MARK_READ:
return MarkReadMailAction()
else:
raise NotImplementedError("Unknown action.") # pragma: nocover
@@ -117,10 +117,10 @@ class MailAccountHandler(LoggingMixin):
return None
def get_title(self, message, att, rule):
if rule.assign_title_from == MailRule.TITLE_FROM_SUBJECT:
if rule.assign_title_from == MailRule.TitleSource.FROM_SUBJECT:
return message.subject
elif rule.assign_title_from == MailRule.TITLE_FROM_FILENAME:
elif rule.assign_title_from == MailRule.TitleSource.FROM_FILENAME:
return os.path.splitext(os.path.basename(att.filename))[0]
else:
@@ -131,20 +131,20 @@ class MailAccountHandler(LoggingMixin):
def get_correspondent(self, message: MailMessage, rule):
c_from = rule.assign_correspondent_from
if c_from == MailRule.CORRESPONDENT_FROM_NOTHING:
if c_from == MailRule.CorrespondentSource.FROM_NOTHING:
return None
elif c_from == MailRule.CORRESPONDENT_FROM_EMAIL:
elif c_from == MailRule.CorrespondentSource.FROM_EMAIL:
return self._correspondent_from_name(message.from_)
elif c_from == MailRule.CORRESPONDENT_FROM_NAME:
elif c_from == MailRule.CorrespondentSource.FROM_NAME:
from_values = message.from_values
if from_values is not None and len(from_values.name) > 0:
return self._correspondent_from_name(from_values.name)
else:
return self._correspondent_from_name(message.from_)
elif c_from == MailRule.CORRESPONDENT_FROM_CUSTOM:
elif c_from == MailRule.CorrespondentSource.FROM_CUSTOM:
return rule.assign_correspondent
else:
@@ -273,7 +273,7 @@ class MailAccountHandler(LoggingMixin):
return total_processed_files
def handle_message(self, message, rule):
def handle_message(self, message, rule) -> int:
if not message.attachments:
return 0
@@ -294,7 +294,8 @@ class MailAccountHandler(LoggingMixin):
if (
not att.content_disposition == "attachment"
and rule.attachment_type == MailRule.ATTACHMENT_TYPE_ATTACHMENTS_ONLY
and rule.attachment_type
== MailRule.AttachmentProcessing.ATTACHMENTS_ONLY
):
self.log(
"debug",
@@ -305,7 +306,12 @@ class MailAccountHandler(LoggingMixin):
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)

View File

@@ -0,0 +1,37 @@
# Generated by Django 4.0.3 on 2022-03-28 17:40
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("paperless_mail", "0008_auto_20210516_0940"),
]
operations = [
migrations.AlterField(
model_name="mailrule",
name="action",
field=models.PositiveIntegerField(
choices=[
(1, "Mark as read, don't process read mails"),
(2, "Flag the mail, don't process flagged mails"),
(3, "Move to specified folder"),
(4, "Delete"),
],
default=3,
verbose_name="action",
),
),
migrations.AlterField(
model_name="mailrule",
name="folder",
field=models.CharField(
default="INBOX",
help_text="Subfolders must be separated by a delimiter, often a dot ('.') or slash ('/'), but it varies by mail server.",
max_length=256,
verbose_name="folder",
),
),
]

View File

@@ -8,15 +8,10 @@ class MailAccount(models.Model):
verbose_name = _("mail account")
verbose_name_plural = _("mail accounts")
IMAP_SECURITY_NONE = 1
IMAP_SECURITY_SSL = 2
IMAP_SECURITY_STARTTLS = 3
IMAP_SECURITY_OPTIONS = (
(IMAP_SECURITY_NONE, _("No encryption")),
(IMAP_SECURITY_SSL, _("Use SSL")),
(IMAP_SECURITY_STARTTLS, _("Use STARTTLS")),
)
class ImapSecurity(models.IntegerChoices):
NONE = 1, _("No encryption")
SSL = 2, _("Use SSL")
STARTTLS = 3, _("Use STARTTLS")
name = models.CharField(_("name"), max_length=256, unique=True)
@@ -34,8 +29,8 @@ class MailAccount(models.Model):
imap_security = models.PositiveIntegerField(
_("IMAP security"),
choices=IMAP_SECURITY_OPTIONS,
default=IMAP_SECURITY_SSL,
choices=ImapSecurity.choices,
default=ImapSecurity.SSL,
)
username = models.CharField(_("username"), max_length=256)
@@ -61,48 +56,25 @@ class MailRule(models.Model):
verbose_name = _("mail rule")
verbose_name_plural = _("mail rules")
ATTACHMENT_TYPE_ATTACHMENTS_ONLY = 1
ATTACHMENT_TYPE_EVERYTHING = 2
class AttachmentProcessing(models.IntegerChoices):
ATTACHMENTS_ONLY = 1, _("Only process attachments.")
EVERYTHING = 2, _("Process all files, including 'inline' " "attachments.")
ATTACHMENT_TYPES = (
(ATTACHMENT_TYPE_ATTACHMENTS_ONLY, _("Only process attachments.")),
(
ATTACHMENT_TYPE_EVERYTHING,
_("Process all files, including 'inline' " "attachments."),
),
)
class AttachmentAction(models.IntegerChoices):
DELETE = 1, _("Mark as read, don't process read mails")
MOVE = 2, _("Flag the mail, don't process flagged mails")
MARK_READ = 3, _("Move to specified folder")
FLAG = 4, _("Delete")
ACTION_DELETE = 1
ACTION_MOVE = 2
ACTION_MARK_READ = 3
ACTION_FLAG = 4
class TitleSource(models.IntegerChoices):
FROM_SUBJECT = 1, _("Use subject as title")
FROM_FILENAME = 2, _("Use attachment filename as title")
ACTIONS = (
(ACTION_MARK_READ, _("Mark as read, don't process read mails")),
(ACTION_FLAG, _("Flag the mail, don't process flagged mails")),
(ACTION_MOVE, _("Move to specified folder")),
(ACTION_DELETE, _("Delete")),
)
TITLE_FROM_SUBJECT = 1
TITLE_FROM_FILENAME = 2
TITLE_SELECTOR = (
(TITLE_FROM_SUBJECT, _("Use subject as title")),
(TITLE_FROM_FILENAME, _("Use attachment filename as title")),
)
CORRESPONDENT_FROM_NOTHING = 1
CORRESPONDENT_FROM_EMAIL = 2
CORRESPONDENT_FROM_NAME = 3
CORRESPONDENT_FROM_CUSTOM = 4
CORRESPONDENT_SELECTOR = (
(CORRESPONDENT_FROM_NOTHING, _("Do not assign a correspondent")),
(CORRESPONDENT_FROM_EMAIL, _("Use mail address")),
(CORRESPONDENT_FROM_NAME, _("Use name (or mail address if not available)")),
(CORRESPONDENT_FROM_CUSTOM, _("Use correspondent selected below")),
)
class CorrespondentSource(models.IntegerChoices):
FROM_NOTHING = 1, _("Do not assign a correspondent")
FROM_EMAIL = 2, _("Use mail address")
FROM_NAME = 3, _("Use name (or mail address if not available)")
FROM_CUSTOM = 4, _("Use correspondent selected below")
name = models.CharField(_("name"), max_length=256, unique=True)
@@ -119,7 +91,10 @@ class MailRule(models.Model):
_("folder"),
default="INBOX",
max_length=256,
help_text=_("Subfolders must be separated by dots."),
help_text=_(
"Subfolders must be separated by a delimiter, often a dot ('.') or"
" slash ('/'), but it varies by mail server.",
),
)
filter_from = models.CharField(
@@ -161,8 +136,8 @@ class MailRule(models.Model):
attachment_type = models.PositiveIntegerField(
_("attachment type"),
choices=ATTACHMENT_TYPES,
default=ATTACHMENT_TYPE_ATTACHMENTS_ONLY,
choices=AttachmentProcessing.choices,
default=AttachmentProcessing.ATTACHMENTS_ONLY,
help_text=_(
"Inline attachments include embedded images, so it's best "
"to combine this option with a filename filter.",
@@ -171,8 +146,8 @@ class MailRule(models.Model):
action = models.PositiveIntegerField(
_("action"),
choices=ACTIONS,
default=ACTION_MARK_READ,
choices=AttachmentAction.choices,
default=AttachmentAction.MARK_READ,
)
action_parameter = models.CharField(
@@ -190,8 +165,8 @@ class MailRule(models.Model):
assign_title_from = models.PositiveIntegerField(
_("assign title from"),
choices=TITLE_SELECTOR,
default=TITLE_FROM_SUBJECT,
choices=TitleSource.choices,
default=TitleSource.FROM_SUBJECT,
)
assign_tag = models.ForeignKey(
@@ -212,8 +187,8 @@ class MailRule(models.Model):
assign_correspondent_from = models.PositiveIntegerField(
_("assign correspondent from"),
choices=CORRESPONDENT_SELECTOR,
default=CORRESPONDENT_FROM_NOTHING,
choices=CorrespondentSource.choices,
default=CorrespondentSource.FROM_NOTHING,
)
assign_correspondent = models.ForeignKey(

View File

@@ -246,13 +246,13 @@ class TestMail(DirectoriesMixin, TestCase):
rule = MailRule(
name="a",
assign_correspondent_from=MailRule.CORRESPONDENT_FROM_NOTHING,
assign_correspondent_from=MailRule.CorrespondentSource.FROM_NOTHING,
)
self.assertIsNone(handler.get_correspondent(message, rule))
rule = MailRule(
name="b",
assign_correspondent_from=MailRule.CORRESPONDENT_FROM_EMAIL,
assign_correspondent_from=MailRule.CorrespondentSource.FROM_EMAIL,
)
c = handler.get_correspondent(message, rule)
self.assertIsNotNone(c)
@@ -264,7 +264,7 @@ class TestMail(DirectoriesMixin, TestCase):
rule = MailRule(
name="c",
assign_correspondent_from=MailRule.CORRESPONDENT_FROM_NAME,
assign_correspondent_from=MailRule.CorrespondentSource.FROM_NAME,
)
c = handler.get_correspondent(message, rule)
self.assertIsNotNone(c)
@@ -275,7 +275,7 @@ class TestMail(DirectoriesMixin, TestCase):
rule = MailRule(
name="d",
assign_correspondent_from=MailRule.CORRESPONDENT_FROM_CUSTOM,
assign_correspondent_from=MailRule.CorrespondentSource.FROM_CUSTOM,
assign_correspondent=someone_else,
)
c = handler.get_correspondent(message, rule)
@@ -289,9 +289,15 @@ class TestMail(DirectoriesMixin, TestCase):
handler = MailAccountHandler()
rule = MailRule(name="a", assign_title_from=MailRule.TITLE_FROM_FILENAME)
rule = MailRule(
name="a",
assign_title_from=MailRule.TitleSource.FROM_FILENAME,
)
self.assertEqual(handler.get_title(message, att, rule), "this_is_the_file")
rule = MailRule(name="b", assign_title_from=MailRule.TITLE_FROM_SUBJECT)
rule = MailRule(
name="b",
assign_title_from=MailRule.TitleSource.FROM_SUBJECT,
)
self.assertEqual(handler.get_title(message, att, rule), "the message title")
def test_handle_message(self):
@@ -302,7 +308,10 @@ class TestMail(DirectoriesMixin, TestCase):
)
account = MailAccount()
rule = MailRule(assign_title_from=MailRule.TITLE_FROM_FILENAME, account=account)
rule = MailRule(
assign_title_from=MailRule.TitleSource.FROM_FILENAME,
account=account,
)
result = self.mail_account_handler.handle_message(message, rule)
@@ -346,7 +355,10 @@ class TestMail(DirectoriesMixin, TestCase):
)
account = MailAccount()
rule = MailRule(assign_title_from=MailRule.TITLE_FROM_FILENAME, account=account)
rule = MailRule(
assign_title_from=MailRule.TitleSource.FROM_FILENAME,
account=account,
)
result = self.mail_account_handler.handle_message(message, rule)
@@ -369,7 +381,10 @@ class TestMail(DirectoriesMixin, TestCase):
)
account = MailAccount()
rule = MailRule(assign_title_from=MailRule.TITLE_FROM_FILENAME, account=account)
rule = MailRule(
assign_title_from=MailRule.TitleSource.FROM_FILENAME,
account=account,
)
result = self.mail_account_handler.handle_message(message, rule)
@@ -392,9 +407,9 @@ class TestMail(DirectoriesMixin, TestCase):
account = MailAccount()
rule = MailRule(
assign_title_from=MailRule.TITLE_FROM_FILENAME,
assign_title_from=MailRule.TitleSource.FROM_FILENAME,
account=account,
attachment_type=MailRule.ATTACHMENT_TYPE_EVERYTHING,
attachment_type=MailRule.AttachmentProcessing.EVERYTHING,
)
result = self.mail_account_handler.handle_message(message, rule)
@@ -409,33 +424,36 @@ class TestMail(DirectoriesMixin, TestCase):
_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(
assign_title_from=MailRule.TITLE_FROM_FILENAME,
assign_title_from=MailRule.TitleSource.FROM_FILENAME,
account=account,
filter_attachment_filename=pattern,
)
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):
@@ -449,7 +467,7 @@ class TestMail(DirectoriesMixin, TestCase):
_ = MailRule.objects.create(
name="testrule",
account=account,
action=MailRule.ACTION_MARK_READ,
action=MailRule.AttachmentAction.MARK_READ,
)
self.assertEqual(len(self.bogus_mailbox.messages), 3)
@@ -472,7 +490,7 @@ class TestMail(DirectoriesMixin, TestCase):
_ = MailRule.objects.create(
name="testrule",
account=account,
action=MailRule.ACTION_DELETE,
action=MailRule.AttachmentAction.DELETE,
filter_subject="Invoice",
)
@@ -493,7 +511,7 @@ class TestMail(DirectoriesMixin, TestCase):
_ = MailRule.objects.create(
name="testrule",
account=account,
action=MailRule.ACTION_FLAG,
action=MailRule.AttachmentAction.FLAG,
filter_subject="Invoice",
)
@@ -516,7 +534,7 @@ class TestMail(DirectoriesMixin, TestCase):
_ = MailRule.objects.create(
name="testrule",
account=account,
action=MailRule.ACTION_MOVE,
action=MailRule.AttachmentAction.MOVE,
action_parameter="spam",
filter_subject="Claim",
)
@@ -562,7 +580,7 @@ class TestMail(DirectoriesMixin, TestCase):
_ = MailRule.objects.create(
name="testrule",
account=account,
action=MailRule.ACTION_MOVE,
action=MailRule.AttachmentAction.MOVE,
action_parameter="spam",
filter_subject="Claim",
)
@@ -583,7 +601,7 @@ class TestMail(DirectoriesMixin, TestCase):
_ = MailRule.objects.create(
name="testrule",
account=account,
action=MailRule.ACTION_MOVE,
action=MailRule.AttachmentAction.MOVE,
action_parameter="spam",
filter_subject="Claim",
order=1,
@@ -592,7 +610,7 @@ class TestMail(DirectoriesMixin, TestCase):
_ = MailRule.objects.create(
name="testrule2",
account=account,
action=MailRule.ACTION_MOVE,
action=MailRule.AttachmentAction.MOVE,
action_parameter="spam",
filter_subject="Claim",
order=2,
@@ -622,7 +640,7 @@ class TestMail(DirectoriesMixin, TestCase):
_ = MailRule.objects.create(
name="testrule",
account=account,
action=MailRule.ACTION_MOVE,
action=MailRule.AttachmentAction.MOVE,
action_parameter="spam",
)
@@ -647,9 +665,9 @@ class TestMail(DirectoriesMixin, TestCase):
name="testrule",
filter_from="amazon@amazon.de",
account=account,
action=MailRule.ACTION_MOVE,
action=MailRule.AttachmentAction.MOVE,
action_parameter="spam",
assign_correspondent_from=MailRule.CORRESPONDENT_FROM_EMAIL,
assign_correspondent_from=MailRule.CorrespondentSource.FROM_EMAIL,
)
self.mail_account_handler.handle_mail_account(account)
@@ -684,7 +702,7 @@ class TestMail(DirectoriesMixin, TestCase):
rule = MailRule.objects.create(
name="testrule3",
account=account,
action=MailRule.ACTION_DELETE,
action=MailRule.AttachmentAction.DELETE,
filter_subject="Claim",
)