mirror of
https://github.com/domainaware/parsedmarc.git
synced 2026-02-22 17:26:22 +00:00
Compare commits
5 Commits
copilot/op
...
copilot/fi
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
eb2218b6fc | ||
|
|
3f2fc5f727 | ||
|
|
f94c28c770 | ||
|
|
c0f05b81b8 | ||
|
|
9c9ef2fa50 |
@@ -29,3 +29,14 @@ token_file = /etc/example/token.json
|
|||||||
include_spam_trash = True
|
include_spam_trash = True
|
||||||
paginate_messages = True
|
paginate_messages = True
|
||||||
scopes = https://www.googleapis.com/auth/gmail.modify
|
scopes = https://www.googleapis.com/auth/gmail.modify
|
||||||
|
|
||||||
|
[msgraph]
|
||||||
|
auth_method = ClientSecret
|
||||||
|
client_id = 12345678-90ab-cdef-1234-567890abcdef
|
||||||
|
client_secret = your-client-secret-here
|
||||||
|
tenant_id = 12345678-90ab-cdef-1234-567890abcdef
|
||||||
|
mailbox = dmarc-reports@example.com
|
||||||
|
# Use standard folder names - they work across all locales
|
||||||
|
# and avoid "Default folder Root not found" errors
|
||||||
|
reports_folder = Inbox
|
||||||
|
archive_folder = Archive
|
||||||
|
|||||||
@@ -229,6 +229,18 @@ The full set of configuration options are:
|
|||||||
username, you must grant the app `Mail.ReadWrite.Shared`.
|
username, you must grant the app `Mail.ReadWrite.Shared`.
|
||||||
:::
|
:::
|
||||||
|
|
||||||
|
:::{tip}
|
||||||
|
When configuring folder names (e.g., `reports_folder`, `archive_folder`),
|
||||||
|
you can use standard folder names like `Inbox`, `Archive`, `Sent Items`, etc.
|
||||||
|
These will be automatically mapped to Microsoft Graph's well-known folder names,
|
||||||
|
which works reliably across different mailbox locales and avoids issues with
|
||||||
|
uninitialized or shared mailboxes. Supported folder names include:
|
||||||
|
- English: Inbox, Sent Items, Deleted Items, Drafts, Junk Email, Archive, Outbox
|
||||||
|
- German: Posteingang, Gesendete Elemente, Gelöschte Elemente, Entwürfe, Junk-E-Mail, Archiv
|
||||||
|
- French: Boîte de réception, Éléments envoyés, Éléments supprimés, Brouillons, Courrier indésirable, Archives
|
||||||
|
- Spanish: Bandeja de entrada, Elementos enviados, Elementos eliminados, Borradores, Correo no deseado
|
||||||
|
:::
|
||||||
|
|
||||||
:::{warning}
|
:::{warning}
|
||||||
If you are using the `ClientSecret` auth method, you need to
|
If you are using the `ClientSecret` auth method, you need to
|
||||||
grant the `Mail.ReadWrite` (application) permission to the
|
grant the `Mail.ReadWrite` (application) permission to the
|
||||||
|
|||||||
@@ -20,6 +20,59 @@ from msgraph.core import GraphClient
|
|||||||
from parsedmarc.log import logger
|
from parsedmarc.log import logger
|
||||||
from parsedmarc.mail.mailbox_connection import MailboxConnection
|
from parsedmarc.mail.mailbox_connection import MailboxConnection
|
||||||
|
|
||||||
|
# Mapping of common folder names to Microsoft Graph well-known folder names
|
||||||
|
# This avoids the "Default folder Root not found" error on uninitialized mailboxes
|
||||||
|
WELL_KNOWN_FOLDER_MAP = {
|
||||||
|
# English names
|
||||||
|
"inbox": "inbox",
|
||||||
|
"sent items": "sentitems",
|
||||||
|
"sent": "sentitems",
|
||||||
|
"sentitems": "sentitems",
|
||||||
|
"deleted items": "deleteditems",
|
||||||
|
"deleted": "deleteditems",
|
||||||
|
"deleteditems": "deleteditems",
|
||||||
|
"trash": "deleteditems",
|
||||||
|
"drafts": "drafts",
|
||||||
|
"junk email": "junkemail",
|
||||||
|
"junk": "junkemail",
|
||||||
|
"junkemail": "junkemail",
|
||||||
|
"spam": "junkemail",
|
||||||
|
"archive": "archive",
|
||||||
|
"outbox": "outbox",
|
||||||
|
"conversation history": "conversationhistory",
|
||||||
|
"conversationhistory": "conversationhistory",
|
||||||
|
# German names
|
||||||
|
"posteingang": "inbox",
|
||||||
|
"gesendete elemente": "sentitems",
|
||||||
|
"gesendet": "sentitems",
|
||||||
|
"gelöschte elemente": "deleteditems",
|
||||||
|
"gelöscht": "deleteditems",
|
||||||
|
"entwürfe": "drafts",
|
||||||
|
"junk-e-mail": "junkemail",
|
||||||
|
"archiv": "archive",
|
||||||
|
"postausgang": "outbox",
|
||||||
|
# French names
|
||||||
|
"boîte de réception": "inbox",
|
||||||
|
"éléments envoyés": "sentitems",
|
||||||
|
"envoyés": "sentitems",
|
||||||
|
"éléments supprimés": "deleteditems",
|
||||||
|
"supprimés": "deleteditems",
|
||||||
|
"brouillons": "drafts",
|
||||||
|
"courrier indésirable": "junkemail",
|
||||||
|
"archives": "archive",
|
||||||
|
"boîte d'envoi": "outbox",
|
||||||
|
# Spanish names
|
||||||
|
"bandeja de entrada": "inbox",
|
||||||
|
"elementos enviados": "sentitems",
|
||||||
|
"enviados": "sentitems",
|
||||||
|
"elementos eliminados": "deleteditems",
|
||||||
|
"eliminados": "deleteditems",
|
||||||
|
"borradores": "drafts",
|
||||||
|
"correo no deseado": "junkemail",
|
||||||
|
"archivar": "archive",
|
||||||
|
"bandeja de salida": "outbox",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
class AuthMethod(Enum):
|
class AuthMethod(Enum):
|
||||||
DeviceCode = 1
|
DeviceCode = 1
|
||||||
@@ -130,6 +183,13 @@ class MSGraphConnection(MailboxConnection):
|
|||||||
self.mailbox_name = mailbox
|
self.mailbox_name = mailbox
|
||||||
|
|
||||||
def create_folder(self, folder_name: str):
|
def create_folder(self, folder_name: str):
|
||||||
|
# Check if this is a well-known folder - they already exist and cannot be created
|
||||||
|
if "/" not in folder_name:
|
||||||
|
well_known_name = WELL_KNOWN_FOLDER_MAP.get(folder_name.lower())
|
||||||
|
if well_known_name:
|
||||||
|
logger.debug(f"Folder '{folder_name}' is a well-known folder, skipping creation")
|
||||||
|
return
|
||||||
|
|
||||||
sub_url = ""
|
sub_url = ""
|
||||||
path_parts = folder_name.split("/")
|
path_parts = folder_name.split("/")
|
||||||
if len(path_parts) > 1: # Folder is a subFolder
|
if len(path_parts) > 1: # Folder is a subFolder
|
||||||
@@ -246,6 +306,12 @@ class MSGraphConnection(MailboxConnection):
|
|||||||
parent_folder_id = folder_id
|
parent_folder_id = folder_id
|
||||||
return self._find_folder_id_with_parent(path_parts[-1], parent_folder_id)
|
return self._find_folder_id_with_parent(path_parts[-1], parent_folder_id)
|
||||||
else:
|
else:
|
||||||
|
# Check if this is a well-known folder name (case-insensitive)
|
||||||
|
well_known_name = WELL_KNOWN_FOLDER_MAP.get(folder_name.lower())
|
||||||
|
if well_known_name:
|
||||||
|
# Use well-known folder name directly to avoid querying uninitialized mailboxes
|
||||||
|
logger.debug(f"Using well-known folder name '{well_known_name}' for '{folder_name}'")
|
||||||
|
return well_known_name
|
||||||
return self._find_folder_id_with_parent(folder_name, None)
|
return self._find_folder_id_with_parent(folder_name, None)
|
||||||
|
|
||||||
def _find_folder_id_with_parent(
|
def _find_folder_id_with_parent(
|
||||||
|
|||||||
26
tests.py
26
tests.py
@@ -156,6 +156,32 @@ class Test(unittest.TestCase):
|
|||||||
parsedmarc.parsed_smtp_tls_reports_to_csv(parsed_report)
|
parsedmarc.parsed_smtp_tls_reports_to_csv(parsed_report)
|
||||||
print("Passed!")
|
print("Passed!")
|
||||||
|
|
||||||
|
def testMSGraphWellKnownFolders(self):
|
||||||
|
"""Test MSGraph well-known folder name mapping"""
|
||||||
|
from parsedmarc.mail.graph import WELL_KNOWN_FOLDER_MAP
|
||||||
|
|
||||||
|
# Test English folder names
|
||||||
|
assert WELL_KNOWN_FOLDER_MAP.get("inbox") == "inbox"
|
||||||
|
assert WELL_KNOWN_FOLDER_MAP.get("sent items") == "sentitems"
|
||||||
|
assert WELL_KNOWN_FOLDER_MAP.get("deleted items") == "deleteditems"
|
||||||
|
assert WELL_KNOWN_FOLDER_MAP.get("archive") == "archive"
|
||||||
|
|
||||||
|
# Test case insensitivity - simulating how the code actually uses it
|
||||||
|
# This is what happens when user config has "reports_folder = Inbox"
|
||||||
|
assert WELL_KNOWN_FOLDER_MAP.get("inbox") == "inbox"
|
||||||
|
assert WELL_KNOWN_FOLDER_MAP.get("Inbox".lower()) == "inbox" # User's exact config
|
||||||
|
assert WELL_KNOWN_FOLDER_MAP.get("INBOX".lower()) == "inbox"
|
||||||
|
assert WELL_KNOWN_FOLDER_MAP.get("Archive".lower()) == "archive"
|
||||||
|
|
||||||
|
# Test German folder names
|
||||||
|
assert WELL_KNOWN_FOLDER_MAP.get("posteingang") == "inbox"
|
||||||
|
assert WELL_KNOWN_FOLDER_MAP.get("Posteingang".lower()) == "inbox" # Capitalized
|
||||||
|
assert WELL_KNOWN_FOLDER_MAP.get("archiv") == "archive"
|
||||||
|
|
||||||
|
# Test that custom folders don't match
|
||||||
|
assert WELL_KNOWN_FOLDER_MAP.get("custom_folder") is None
|
||||||
|
assert WELL_KNOWN_FOLDER_MAP.get("my_reports") is None
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
unittest.main(verbosity=2)
|
unittest.main(verbosity=2)
|
||||||
|
|||||||
Reference in New Issue
Block a user