This commit is contained in:
huangzhenpc
2025-02-26 18:48:31 +08:00
parent ce450544b1
commit d3797bcb60

View File

@@ -2,6 +2,7 @@ import smtplib
import os import os
import json import json
import logging import logging
import sys
from email.parser import BytesParser from email.parser import BytesParser
from email.policy import default from email.policy import default
from datetime import datetime from datetime import datetime
@@ -11,37 +12,57 @@ import asyncore
import base64 import base64
from .config import Config from .config import Config
# 配置日志 # 配置日志输出到控制台
logging.basicConfig( logging.basicConfig(
level=logging.INFO, level=logging.DEBUG, # 改为 DEBUG 级别
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s' format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
handlers=[
logging.StreamHandler(sys.stdout)
]
) )
logger = logging.getLogger('smtp_server') logger = logging.getLogger('smtp_server')
# 初始化 Redis 客户端 # 初始化 Redis 客户端
redis_client = redis.from_url(Config.REDIS_URL) redis_client = redis.from_url(Config.REDIS_URL)
class CustomSMTPServer(smtpd.SMTPServer): class CustomSMTPServer(smtpd.SMTPServer):
def __init__(self, localaddr, remoteaddr):
logger.info(f"Initializing SMTP server on {localaddr}")
super().__init__(localaddr, remoteaddr)
def process_message(self, peer, mailfrom, rcpttos, data): def process_message(self, peer, mailfrom, rcpttos, data):
try: try:
logger.debug(f"Connection from peer: {peer}")
logger.debug(f"Mail from: {mailfrom}")
logger.debug(f"Recipients: {rcpttos}")
logger.debug(f"Raw data length: {len(data)} bytes")
# 记录接收到的邮件基本信息 # 记录接收到的邮件基本信息
logger.info(f"Received mail from {mailfrom} to {rcpttos}") logger.info(f"Received mail from {mailfrom} to {rcpttos}")
# 验证收件人域名 # 验证收件人域名
valid_recipients = []
for rcpt in rcpttos: for rcpt in rcpttos:
if not rcpt.endswith('@nosqli.com'): if not rcpt.endswith('@nosqli.com'):
logger.warning(f"Rejected mail to {rcpt}: invalid domain") logger.warning(f"Rejected mail to {rcpt}: invalid domain")
continue else:
valid_recipients.append(rcpt)
if not valid_recipients:
logger.error("No valid recipients found")
return
# 解析邮件 # 解析邮件
logger.debug("Parsing email data...")
email = BytesParser(policy=default).parsebytes(data) email = BytesParser(policy=default).parsebytes(data)
# 获取邮件正文 # 获取邮件正文
body = self._get_email_body(email) body = self._get_email_body(email)
logger.debug(f"Email body length: {len(body) if body else 0}")
# 处理附件 # 处理附件
attachments = self._process_attachments(email) attachments = self._process_attachments(email)
logger.debug(f"Found {len(attachments)} attachments")
# 构建邮件数据 # 构建邮件数据
timestamp = datetime.now().isoformat() timestamp = datetime.now().isoformat()
@@ -51,7 +72,7 @@ class CustomSMTPServer(smtpd.SMTPServer):
'message_id': message_id, 'message_id': message_id,
'subject': email.get('subject', ''), 'subject': email.get('subject', ''),
'sender': mailfrom, 'sender': mailfrom,
'recipients': json.dumps(rcpttos), 'recipients': json.dumps(valid_recipients),
'body': body, 'body': body,
'timestamp': timestamp, 'timestamp': timestamp,
'attachments': json.dumps(attachments), 'attachments': json.dumps(attachments),
@@ -71,12 +92,18 @@ class CustomSMTPServer(smtpd.SMTPServer):
def _get_email_body(self, email): def _get_email_body(self, email):
"""提取邮件正文""" """提取邮件正文"""
try: try:
logger.debug("Extracting email body...")
if email.is_multipart(): if email.is_multipart():
for part in email.walk(): for part in email.walk():
if part.get_content_type() == "text/plain": if part.get_content_type() == "text/plain":
return part.get_payload(decode=True).decode() content = part.get_payload(decode=True).decode()
logger.debug(f"Found text/plain content: {len(content)} chars")
return content
else: else:
return email.get_payload(decode=True).decode() content = email.get_payload(decode=True).decode()
logger.debug(f"Found single part content: {len(content)} chars")
return content
logger.warning("No text content found in email")
return "" return ""
except Exception as e: except Exception as e:
logger.error(f"Error extracting email body: {str(e)}") logger.error(f"Error extracting email body: {str(e)}")
@@ -86,6 +113,7 @@ class CustomSMTPServer(smtpd.SMTPServer):
"""处理邮件附件""" """处理邮件附件"""
attachments = [] attachments = []
try: try:
logger.debug("Processing attachments...")
if email.is_multipart(): if email.is_multipart():
for part in email.walk(): for part in email.walk():
if part.get_content_maintype() == 'multipart': if part.get_content_maintype() == 'multipart':
@@ -95,6 +123,7 @@ class CustomSMTPServer(smtpd.SMTPServer):
filename = part.get_filename() filename = part.get_filename()
if filename: if filename:
logger.debug(f"Processing attachment: {filename}")
attachment_data = part.get_payload(decode=True) attachment_data = part.get_payload(decode=True)
attachments.append({ attachments.append({
'filename': filename, 'filename': filename,
@@ -102,6 +131,7 @@ class CustomSMTPServer(smtpd.SMTPServer):
'content_type': part.get_content_type(), 'content_type': part.get_content_type(),
'size': len(attachment_data) 'size': len(attachment_data)
}) })
logger.debug(f"Attachment processed: {filename} ({len(attachment_data)} bytes)")
except Exception as e: except Exception as e:
logger.error(f"Error processing attachments: {str(e)}") logger.error(f"Error processing attachments: {str(e)}")
return attachments return attachments
@@ -109,22 +139,27 @@ class CustomSMTPServer(smtpd.SMTPServer):
def _store_email(self, email_data): def _store_email(self, email_data):
"""存储邮件到 Redis""" """存储邮件到 Redis"""
try: try:
logger.debug("Storing email in Redis...")
# 使用 message_id 作为主键 # 使用 message_id 作为主键
email_key = f"email:{email_data['message_id']}" email_key = f"email:{email_data['message_id']}"
redis_client.hmset(email_key, email_data) redis_client.hmset(email_key, email_data)
logger.debug(f"Stored email with key: {email_key}")
# 为每个收件人创建索引 # 为每个收件人创建索引
recipients = json.loads(email_data['recipients']) recipients = json.loads(email_data['recipients'])
for recipient in recipients: for recipient in recipients:
recipient_key = f"recipient:{recipient}" recipient_key = f"recipient:{recipient}"
redis_client.lpush(recipient_key, email_key) redis_client.lpush(recipient_key, email_key)
logger.debug(f"Created recipient index: {recipient_key}")
# 创建时间索引 # 创建时间索引
time_key = f"time:{email_data['timestamp']}" time_key = f"time:{email_data['timestamp']}"
redis_client.set(time_key, email_key) redis_client.set(time_key, email_key)
logger.debug(f"Created time index: {time_key}")
# 设置过期时间可选这里设置为30天 # 设置过期时间可选这里设置为30天
redis_client.expire(email_key, 30 * 24 * 60 * 60) redis_client.expire(email_key, 30 * 24 * 60 * 60)
logger.debug("Set expiration time: 30 days")
except Exception as e: except Exception as e:
logger.error(f"Error storing email: {str(e)}") logger.error(f"Error storing email: {str(e)}")
@@ -136,6 +171,7 @@ def start_smtp_server(host='0.0.0.0', port=25):
try: try:
logger.info(f"Starting SMTP server on {host}:{port}") logger.info(f"Starting SMTP server on {host}:{port}")
server = CustomSMTPServer((host, port), None) server = CustomSMTPServer((host, port), None)
logger.info("SMTP server initialized, entering main loop...")
asyncore.loop() asyncore.loop()
except Exception as e: except Exception as e:
logger.error(f"Error starting SMTP server: {str(e)}") logger.error(f"Error starting SMTP server: {str(e)}")