Files
emailsystem/app/services/smtp_server.py
2025-02-25 19:50:00 +08:00

136 lines
4.6 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import asyncio
import logging
import email
import platform
from email.policy import default
from aiosmtpd.controller import Controller
from aiosmtpd.smtp import SMTP as SMTPProtocol
from aiosmtpd.handlers import Message
import os
import sys
import threading
from ..models.domain import Domain
from ..models.mailbox import Mailbox
logger = logging.getLogger(__name__)
# 检测是否Windows环境
IS_WINDOWS = platform.system().lower() == 'windows'
class EmailHandler(Message):
"""处理接收的电子邮件"""
def __init__(self, mail_store):
super().__init__()
self.mail_store = mail_store
def handle_message(self, message):
"""处理邮件消息这是Message类的抽象方法必须实现"""
# 这个方法在异步DATA处理完成后被调用但我们的邮件处理逻辑已经在handle_DATA中实现
# 所以这里只是一个空实现
return
async def handle_DATA(self, server, session, envelope):
"""处理接收到的邮件数据"""
try:
# 获取收件人和发件人
peer = session.peer
mail_from = envelope.mail_from
rcpt_tos = envelope.rcpt_tos
# 获取原始邮件内容
data = envelope.content
mail = email.message_from_bytes(data, policy=default)
# 保存邮件到存储服务
for rcpt in rcpt_tos:
result = await self.mail_store.save_email(mail_from, rcpt, mail, data)
# 记录日志
if result:
logger.info(f"邮件已保存: {mail_from} -> {rcpt}, 主题: {mail.get('Subject')}")
else:
logger.warning(f"邮件未保存: {mail_from} -> {rcpt}, 可能是无效地址")
return '250 Message accepted for delivery'
except Exception as e:
logger.error(f"处理邮件时出错: {str(e)}")
return '451 Requested action aborted: error in processing'
# 为Windows环境自定义SMTP控制器
if IS_WINDOWS:
class WindowsSafeController(Controller):
"""Windows环境安全的Controller跳过连接测试"""
def _trigger_server(self):
"""Windows环境下跳过SMTP服务器自检连接测试"""
# 在Windows环境下我们跳过自检连接测试
logger.info("Windows环境: 跳过SMTP服务器连接自检")
return
class SMTPServer:
"""SMTP服务器实现"""
def __init__(self, host='0.0.0.0', port=25, mail_store=None):
self.host = host
self.port = port
self.mail_store = mail_store
self.controller = None
self.server_thread = None
def start(self):
"""启动SMTP服务器"""
if self.controller:
logger.warning("SMTP服务器已经在运行")
return
try:
handler = EmailHandler(self.mail_store)
# 根据环境选择适当的Controller
if IS_WINDOWS:
# Windows环境使用自定义Controller
logger.info(f"Windows环境: 使用自定义Controller启动SMTP服务器 {self.host}:{self.port}")
self.controller = WindowsSafeController(
handler,
hostname=self.host,
port=self.port
)
else:
# 非Windows环境使用标准Controller
self.controller = Controller(
handler,
hostname=self.host,
port=self.port
)
# 在单独的线程中启动服务器
self.server_thread = threading.Thread(
target=self.controller.start,
daemon=True
)
self.server_thread.start()
logger.info(f"SMTP服务器已启动在 {self.host}:{self.port}")
return True
except Exception as e:
logger.error(f"启动SMTP服务器失败: {str(e)}")
return False
def stop(self):
"""停止SMTP服务器"""
if not self.controller:
logger.warning("SMTP服务器没有运行")
return
try:
self.controller.stop()
self.controller = None
self.server_thread = None
logger.info("SMTP服务器已停止")
return True
except Exception as e:
logger.error(f"停止SMTP服务器失败: {str(e)}")
return False