You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
"""Check if any or all of the elements in the `query_list` is in the email `field`.
246
+
247
+
Arguments:
248
+
query_list (list[str] | str): list of keywords or unique keyword to find in `field`.
249
+
field (str): any RFC 822 header or `"body"`.
250
+
case_sensitive (str): `True` if the search should be case-sensitive. This has no effect if `field` is a RFC 822 header, it only applies to the email body.
251
+
mode (str): `"any"` if any element in `query_list` should be found in `field` to return `True`. `"all"` if all elements in `query_list` should be found in `field` to return `True`.
252
+
253
+
Returns:
254
+
(bool): `True` if any or all elements (depending on `mode`) of `query_list` have been found in `field`.
"""Add any arbitrary IMAP tag (aka label), standard or not, to the current email.
284
+
285
+
Warning:
286
+
In Mozilla Thunderbird, labels/tags need to be configured first in the preferences (by mapping the label string to a color) to properly appear in the GUI. Otherwise, any undefined tag will be identified as "Important" (associated with red), no matter its actual string.
287
+
288
+
Horde, Roundcube and Nextcloud mail (based on Horde) treat those properly.
# Delete an email directly without using the trash bin.
303
-
# Use a move to trash folder to get a last chance at reviewing what will be deleted.
317
+
"""Delete the current email directly without using the trash bin. It will not be recoverable.
318
+
319
+
Use [EMail.move][protocols.imap_object.EMail.move] to move the email to the trash folder to get a last chance at reviewing what will be deleted.
320
+
321
+
Note:
322
+
As per IMAP standard, this only add the `\\Deleted` flag to the current email. Emails will be actually deleted when the `expunge` server command is launched, which is done automatically at the end of [Server.run_filters][protocols.imap_server.Server.run_filters].
"""Move the current email to the target `folder`, that will be created recursively if it does not exist. `folder` will be internally encoded to IMAP-custom UTF-7 with [Server.encode_imap_folder][protocols.imap_server.Server.encode_imap_folder].
# if you answer programmatically, you need to manually pass the Message-ID of the original email
380
-
# to the In-Reply-To and Referencess of the answer to get threaded messages. In-Reply-To gets only
381
-
# the immediate previous email, References get the whole thread.
409
+
"""Flag or unflag an email as answered.
382
410
411
+
Arguments:
412
+
mode (str): `add` to add the `\\Answered` IMAP tag to the current email, `remove` to remove it.
413
+
414
+
Note :
415
+
if you answer programmatically, you need to manually pass the Message-ID of the original email to the In-Reply-To and Referencess of the answer to get threaded messages. In-Reply-To gets only the immediate previous email, References get the whole thread.
# Check if any of the servers listed by IP in the "Received" header is authorized
445
-
# by the mail server to send emails on behalf of the email address used as "From".
487
+
"""Check if any of the servers listed in the `Received` email headers is authorized by the DNS SPF rules to send emails on behalf of the email address set in `Return-Path`.
488
+
489
+
Returns:
490
+
score:
491
+
- `= 0`: neutral result, no explicit success or fail, or server configuration could not be retrieved/interpreted.
492
+
- `> 0`: success, server is explicitly authorized or SPF rules are deliberately permissive.
493
+
- `< 0`: fail, server is unauthorized.
494
+
- `= 2`: explicit success, server is authorized.
495
+
- `= -2`: explicit fail, server is forbidden, the email is a deliberate spoofing attempt.
496
+
497
+
Note:
498
+
The `Return-Path` header is set by any proper mail client to the mailbox collecting bounces (notice of undelivered emails), and, while it is optional, the [RFC 4408](https://www.ietf.org/rfc/rfc4408.txt) states that it is the one from which the SPF domain will be inferred. In practice, it is missing only in certain spam messages, so its absence is treated as an explicit fail.
499
+
500
+
Warning:
501
+
Emails older than 6 months will at least get a score of `0` and will therefore never fail the SPF check. This is because DNS configuration may have changed since the email was sent, and it could have been valid at the time of sending.
502
+
"""
446
503
# See https://www.rfc-editor.org/rfc/rfc7208
447
504
# Output a reputation score :
448
505
scores= { "none": 0, # no SPF records were retrieved from the DNS.
@@ -514,14 +571,22 @@ def spf_pass(self) -> int:
514
571
# Otherwise return neutral score. This is because server DKIM/ARC keys, MX and SPF may have changed.
# -2 if the DKIM signature is invalid. That's because many spammers
523
-
# forge a fake Google DKIM signature hoping to past by the spam filters
524
-
# that only check for the header presence without actually validating it.
574
+
defdkim_pass(self) ->int:
575
+
"""Check the authenticity of the DKIM signature.
576
+
577
+
Note:
578
+
The DKIM signature uses an asymetric key scheme, where the private key is set on the SMTP server and the public key is set in DNS records of the mailserver. The signature is a cryptographic hash of the email headers (not their content). A valid signature means the private key used to hash headers matches the public key in the DNS records AND the headers have not been tampered with since sending.
579
+
580
+
Returns:
581
+
score:
582
+
- `= 0`: there is no DKIM signature.
583
+
- `= 1`: the DKIM signature is valid but outdated. This means the public key in DNS records has been updated since they email was sent.
584
+
- `= 2`: the DKIM signature is valid and up-to-date.
585
+
- `= -2`: the DKIM signature is invalid. Either the headers have been tampered or the DKIM signature is entirely forged (happens a lot in spam emails).
586
+
587
+
Warning:
588
+
Emails older than 6 months will at least get a score of `0` and will therefore never fail the DKIM check. This is because DNS configuration (public key) may have changed since the email was sent, and it could have been valid at the time of sending.
589
+
"""
525
590
526
591
ifself.has_header("DKIM-Signature"):
527
592
dkim_score=-2
@@ -553,14 +618,18 @@ def dkim_pass(self):
553
618
# Otherwise return neutral score. This is because server DKIM/ARC keys, MX and SPF may have changed.
# -2 if the ARC signature is invalid. That's because many spammers
561
-
# forge a fake Google ARC signature hoping to past by the spam filters
562
-
# that only check for the header presence without actually validating it.
621
+
defarc_pass(self) ->int:
622
+
"""Check the authenticity of the ARC signature.
623
+
624
+
Note:
625
+
The ARC signature is still experimental and not widely used. When an email is forwarded, by an user or through a mailing list, its DKIM signature will be invalidated and the email will appear forged/tampered. ARC authentifies the intermediate servers and aims at solving this issue.
563
626
627
+
Returns:
628
+
score:
629
+
- `= 0`: there is no ARC signature,
630
+
- `= 2`: the ARC signature is valid
631
+
- `=-2`: the ARC signature is invalid. Typically, it means the signature has been forged.
# < 0 : spoofed, either or both SPF and DKIM explicitely failed
657
+
"""Compute the score of authenticity of the email, summing the results of [EMail.spf_pass][protocols.imap_object.EMail.spf_pass], [EMail.dkim_pass][protocols.imap_object.EMail.dkim_pass] and [EMail.arc_pass][protocols.imap_object.EMail.arc_pass]. The weighting is designed such that one valid check compensates one fail.
658
+
659
+
Returns:
660
+
score:
661
+
- `== 0`: neutral, no explicit authentification is defined on DNS or no rule could be found
662
+
- `> 0`: explicitly authenticated by at least one method,
663
+
- `== 6`: maximal authenticity (valid SPF, DKIM and ARC)
664
+
- `< 0`: spoofed, at least one of SPF or DKIM or ARC failed and
# Check SPF and DKIM to validate that the email is authentic,
602
-
# aka not spoofed. That's enough to detect most spams.
674
+
"""Helper function for [EMail.authenticity_score][protocols.imap_object.EMail.authenticity_score], checking if at least one authentication method succeeded.
675
+
676
+
Returns:
677
+
True if [EMail.authenticity_score][protocols.imap_object.EMail.authenticity_score] returns a score greater or equal to zero.
678
+
"""
603
679
returnself.authenticity_score() >=0
604
680
605
681
##
606
682
## Utils
607
683
##
608
684
609
-
defage(self):
610
-
# Compute the age of an email at the time of evaluation
685
+
defage(self) ->timedelta:
686
+
"""Compute the age of an email at the time of evaluation
687
+
688
+
Returns:
689
+
time difference between current time and sending time of the email
690
+
"""
611
691
current_date=datetime.now(timezone.utc)
612
692
delta= (current_date-self.date)
613
693
returndelta
614
694
615
695
616
-
defnow(self):
617
-
# Helper to get access to date/time from within the email object when writing filters
696
+
defnow(self)->str:
697
+
"""Helper to get access to date/time from within the email object when writing filters"""
618
698
returnutils.now()
619
699
620
700
@@ -684,10 +764,14 @@ def create_hash(self):
684
764
self.hash=timestamp+"-"+hash
685
765
686
766
687
-
defquery_referenced_emails(self) ->list:
688
-
# Fetch the list of all emails referenced in the present message,
689
-
# aka the whole email thread in wich the current email belongs.
690
-
# The list is sorted from newest to oldest.
767
+
defquery_referenced_emails(self) ->list[EMail]:
768
+
"""Fetch the list of all emails referenced in the present message, aka the whole email thread in wich the current email belongs.
769
+
770
+
The list is sorted from newest to oldest. Queries emails having a `Message-ID` header matching the ones contained in the `References` header of the current email.
0 commit comments