-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsmtp_dumper.py
106 lines (87 loc) · 3.62 KB
/
smtp_dumper.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
#!/usr/bin/env python3
# Configuration
CONFIG = {
'host': '0.0.0.0', # Listen on all interfaces
'port': 2525, # Port to listen on
'attachment_dir': '/var/smtp-dumper/attachments', # Directory to save attachments
'log_dir': '/var/smtp-dumper/logs' # Directory to save logs
}
import asyncio
import logging
import os
import sys
from aiosmtpd.controller import Controller
from aiosmtpd.smtp import SMTP
from email import message_from_bytes
from datetime import datetime
# Configure logging before anything else
os.makedirs(CONFIG['log_dir'], exist_ok=True)
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s',
handlers=[
logging.FileHandler(os.path.join(CONFIG['log_dir'], 'smtp_dumper.log')),
logging.StreamHandler(sys.stdout)
]
)
logger = logging.getLogger('smtp_dumper')
# Suppress aiosmtpd debug logs
logging.getLogger('mail.log').setLevel(logging.WARNING)
logging.getLogger('aiosmtpd').setLevel(logging.WARNING)
class AttachmentHandler:
def __init__(self):
self.save_dir = CONFIG['attachment_dir']
os.makedirs(self.save_dir, exist_ok=True)
async def handle_RCPT(self, server, session, envelope, address, rcpt_options):
envelope.rcpt_tos.append(address)
return '250 OK'
async def handle_DATA(self, server, session, envelope):
try:
message = message_from_bytes(envelope.content)
# Process each attachment
attachments_found = False
for part in message.walk():
if part.get_content_maintype() == 'multipart':
continue
filename = part.get_filename()
if filename:
attachments_found = True
# Generate unique filename with timestamp
timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
unique_filename = f"{timestamp}_{filename}"
filepath = os.path.join(self.save_dir, unique_filename)
# Save attachment
with open(filepath, 'wb') as f:
f.write(part.get_payload(decode=True))
logger.info(f"Saved attachment: {unique_filename}")
if not attachments_found:
logger.info("Email received but no attachments found")
return '250 Message accepted for delivery'
except Exception as e:
logger.error(f"Error processing message: {str(e)}")
return '500 Error processing message'
async def run_server():
# Create handler and controller
handler = AttachmentHandler()
controller = Controller(handler, hostname=CONFIG['host'], port=CONFIG['port'])
try:
# Start the SMTP server
controller.start()
logger.info(f"Started SMTP server on {CONFIG['host']}:{CONFIG['port']}")
logger.info(f"Saving attachments to {os.path.abspath(CONFIG['attachment_dir'])}")
logger.info(f"Logs available at {os.path.abspath(CONFIG['log_dir'])}")
# Keep the server running indefinitely
forever = asyncio.Event()
await forever.wait()
except Exception as e:
logger.error(f"Server error: {str(e)}")
sys.exit(1)
finally:
controller.stop()
logger.info("Server stopped")
if __name__ == '__main__':
try:
asyncio.run(run_server())
except KeyboardInterrupt:
logger.info("Server shutdown requested")
sys.exit(0)