#!/usr/bin/env python3 """ sync_dynamodb_to_sieve.py - Sync DynamoDB rules to Dovecot Sieve """ import boto3 import os from pathlib import Path # Config REGION = 'us-east-2' TABLE = 'email-rules' VMAIL_BASE = '/var/mail' dynamodb = boto3.resource('dynamodb', region_name=REGION) table = dynamodb.Table(TABLE) def generate_sieve(email, rules): """Generate Sieve script from DynamoDB rules""" script = [ 'require ["copy","vacation","variables"];', '', '# Skip if already processed by worker', 'if header :contains "X-SES-Worker-Processed" "" {', ' keep;', ' stop;', '}', '' ] # Forwards forwards = rules.get('forwards', []) if forwards: script.append('# rule:[forward]') for fwd in forwards: script.append(f'redirect :copy "{fwd}";') # OOO if rules.get('ooo_active'): msg = rules.get('ooo_message', 'I am away') script.append('# rule:[reply]') script.append(f'vacation :days 1 :from "{email}" "{msg}";') return '\n'.join(script) def sync(): """Sync all rules from DynamoDB to Sieve""" response = table.scan() for item in response.get('Items', []): email = item['email_address'] domain = email.split('@')[1] user = email.split('@')[0] # Path: /var/mail/domain.de/user/.dovecot.sieve mailbox_dir = Path(VMAIL_BASE) / domain / user # Skip if mailbox doesn't exist if not mailbox_dir.exists(): print(f'⚠ Skipped {email} (mailbox not found)') continue sieve_path = mailbox_dir / '.dovecot.sieve' # Generate & write script = generate_sieve(email, item) sieve_path.write_text(script) # Compile os.system(f'sievec {sieve_path}') # Set ownership os.system(f'chown docker:docker {sieve_path}') svbin_path = f'{sieve_path}.svbin' if os.path.exists(svbin_path): os.system(f'chown docker:docker {svbin_path}') print(f'✓ {email}') if __name__ == '__main__': sync()