136 lines
4.6 KiB
Python
136 lines
4.6 KiB
Python
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 |