aerc sendmail wip

This commit is contained in:
Tim Bendt
2025-08-11 09:44:47 -05:00
parent 5eddddc8ec
commit c64fbbb072
6 changed files with 561 additions and 13 deletions

152
sendmail Executable file
View File

@@ -0,0 +1,152 @@
#!/usr/bin/env python3
"""
Sendmail-compatible wrapper for Microsoft Graph email sending.
Queues emails in maildir format for processing by the sync daemon.
"""
import sys
import os
import time
import logging
from email.parser import Parser
from email.utils import parseaddr
# Add the project root to Python path
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
from src.utils.mail_utils.helpers import ensure_directory_exists
def extract_org_from_email(email_address: str) -> str:
"""
Extract organization name from email address domain.
Args:
email_address: Email address like "user@corteva.com"
Returns:
Organization name (e.g., "corteva")
"""
if "@" not in email_address:
return "default"
domain = email_address.split("@")[1].lower()
# Map known domains to org names
domain_to_org = {
"corteva.com": "corteva",
# Add more domain mappings as needed
}
return domain_to_org.get(domain, domain.split(".")[0])
def create_outbox_structure(base_path: str, org: str):
"""
Create maildir structure for outbox.
Args:
base_path: Base maildir path (e.g., ~/Mail)
org: Organization name
"""
org_path = os.path.join(base_path, org, "outbox")
ensure_directory_exists(os.path.join(org_path, "new"))
ensure_directory_exists(os.path.join(org_path, "cur"))
ensure_directory_exists(os.path.join(org_path, "tmp"))
ensure_directory_exists(os.path.join(org_path, "failed"))
def queue_email(email_content: str, org: str) -> bool:
"""
Queue email in maildir outbox for sending.
Args:
email_content: Raw email content
org: Organization name
Returns:
True if queued successfully, False otherwise
"""
try:
# Get base maildir path
base_path = os.path.expanduser(os.getenv("MAILDIR_PATH", "~/Mail"))
# Create outbox structure
create_outbox_structure(base_path, org)
# Generate unique filename
timestamp = str(int(time.time() * 1000000))
hostname = os.uname().nodename
filename = f"{timestamp}.{os.getpid()}.{hostname}"
# Write to tmp first, then move to new (atomic operation)
tmp_path = os.path.join(base_path, org, "outbox", "tmp", filename)
new_path = os.path.join(base_path, org, "outbox", "new", filename)
with open(tmp_path, "w", encoding="utf-8") as f:
f.write(email_content)
os.rename(tmp_path, new_path)
return True
except Exception as e:
logging.error(f"Failed to queue email: {e}")
return False
def main():
"""
Main sendmail wrapper function.
Reads email from stdin and queues it for sending.
"""
# Set up basic logging
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
handlers=[
logging.FileHandler(os.path.expanduser("~/Mail/sendmail.log")),
]
)
try:
# Read email from stdin
email_content = sys.stdin.read()
if not email_content.strip():
logging.error("No email content received")
sys.exit(1)
# Parse email to extract From header
parser = Parser()
msg = parser.parsestr(email_content)
from_header = msg.get("From", "")
if not from_header:
logging.error("No From header found in email")
sys.exit(1)
# Extract email address from From header
_, from_email = parseaddr(from_header)
if not from_email:
logging.error(f"Could not parse email address from From header: {from_header}")
sys.exit(1)
# Determine organization from email domain
org = extract_org_from_email(from_email)
# Queue the email
if queue_email(email_content, org):
logging.info(f"Email queued successfully for org: {org}, from: {from_email}")
sys.exit(0)
else:
logging.error("Failed to queue email")
sys.exit(1)
except Exception as e:
logging.error(f"Sendmail wrapper error: {e}")
sys.exit(1)
if __name__ == "__main__":
main()