Files
PyMTA-server/email_server/server_runner.py
2025-05-31 21:14:05 +01:00

142 lines
4.9 KiB
Python

"""
Modular SMTP Server with DKIM support.
Main server file that ties all modules together.
"""
import asyncio
from email_server.settings_loader import load_settings
from email_server.tool_box import get_logger
# Import our modules
from email_server.models import create_tables
from email_server.smtp_handler import EnhancedCustomSMTPHandler, PlainController
from email_server.tls_utils import generate_self_signed_cert, create_ssl_context
from email_server.dkim_manager import DKIMManager
from aiosmtpd.controller import Controller
from aiosmtpd.smtp import SMTP as AIOSMTP
settings = load_settings()
SMTP_PORT = int(settings['Server']['SMTP_PORT'])
SMTP_TLS_PORT = int(settings['Server']['SMTP_TLS_PORT'])
HOSTNAME = settings['Server']['HOSTNAME']
LOG_LEVEL = settings['Logging']['LOG_LEVEL']
BIND_IP = settings['Server']['BIND_IP']
logger = get_logger()
# Enable asyncio debugging
try:
loop = asyncio.get_running_loop()
loop.set_debug(True)
except RuntimeError:
# No running loop, set debug when we create one
pass
async def start_server():
"""Main server function."""
logger.debug("Starting SMTP Server with DKIM support...")
# Initialize database
logger.debug("Initializing database...")
create_tables()
# Initialize DKIM manager and generate keys for domains without them
logger.debug("Initializing DKIM manager...")
dkim_manager = DKIMManager()
dkim_manager.initialize_default_keys()
# Add test data if needed
from .models import Session, Domain, User, WhitelistedIP, hash_password
session = Session()
try:
# Add example.com domain if not exists
domain = session.query(Domain).filter_by(domain_name='example.com').first()
if not domain:
domain = Domain(domain_name='example.com')
session.add(domain)
session.commit()
logger.debug("Added example.com domain")
# Add test user if not exists
user = session.query(User).filter_by(email='test@example.com').first()
if not user:
user = User(
email='test@example.com',
password_hash=hash_password('testpass123'),
domain_id=domain.id
)
session.add(user)
session.commit()
logger.debug("Added test user: test@example.com")
# Add whitelisted IP if not exists
whitelist = session.query(WhitelistedIP).filter_by(ip_address='127.0.0.1').first()
if not whitelist:
whitelist = WhitelistedIP(ip_address='127.0.0.1', domain_id=domain.id)
session.add(whitelist)
session.commit()
logger.debug("Added whitelisted IP: 127.0.0.1")
except Exception as e:
session.rollback()
logger.error(f"Error adding test data: {e}")
finally:
session.close()
# Generate SSL certificate if it doesn't exist
logger.debug("Checking SSL certificates...")
if not generate_self_signed_cert():
logger.error("Failed to generate SSL certificate")
return
# Create SSL context
ssl_context = create_ssl_context()
if not ssl_context:
logger.error("Failed to create SSL context")
return
# Start plain SMTP server (with IP whitelist fallback)
handler_plain = EnhancedCustomSMTPHandler()
controller_plain = PlainController(
handler_plain,
hostname=BIND_IP,
server_hostname="TestEnvironment",
port=SMTP_PORT
)
controller_plain.start()
logger.debug(f'Starting plain SMTP server on {HOSTNAME}:{SMTP_PORT}...')
# Start TLS SMTP server using closure pattern like the original
handler_tls = EnhancedCustomSMTPHandler()
# Define TLS controller class with ssl_context in closure (like original)
class TLSController(Controller):
def factory(self):
return AIOSMTP(
self.handler,
tls_context=ssl_context, # Use ssl_context from closure
require_starttls=False, # Don't force STARTTLS, but make it available
auth_require_tls=True, # If auth is used, require TLS
authenticator=self.handler.combined_authenticator,
decode_data=True,
hostname=self.hostname
)
controller_tls = TLSController(
handler_tls,
hostname=BIND_IP,
server_hostname="TestEnvironment",
port=SMTP_TLS_PORT
)
controller_tls.start()
logger.debug(f' - Plain SMTP (IP whitelist): {BIND_IP}:{SMTP_PORT}')
logger.debug(f' - STARTTLS SMTP (auth required): {BIND_IP}:{SMTP_TLS_PORT}')
logger.debug('Management commands:')
logger.debug(' python cli_tools.py --help')
try:
await asyncio.Event().wait()
except KeyboardInterrupt:
logger.debug('Shutting down SMTP servers...')
controller_plain.stop()
controller_tls.stop()
logger.debug('SMTP servers stopped.')