server ready
This commit is contained in:
parent
0428ecad50
commit
7aa854f281
31
README.md
31
README.md
|
@ -1,5 +1,32 @@
|
|||
# Open Trashmail
|
||||
Open source / selfhostable trashmail solution
|
||||
<p align="center">
|
||||
<a href="" rel="noopener">
|
||||
<img height=200px src="https://pictshare.net/phhynj.png" alt="PictShare logo"></a>
|
||||
</p>
|
||||
|
||||
<h1 align="center">Open Trashmail</h1>
|
||||
|
||||
<div align="center">
|
||||
|
||||
![](https://img.shields.io/badge/php-7.1%2B-brightgreen.svg)
|
||||
![](https://img.shields.io/badge/python-2.7%2B-brightgreen.svg)
|
||||
[![Apache License](https://img.shields.io/badge/license-Apache-blue.svg?style=flat)](https://github.com/HaschekSolutions/opentrashmail/blob/master/LICENSE)
|
||||
[![HitCount](http://hits.dwyl.io/HaschekSolutions/opentrashmail.svg)](http://hits.dwyl.io/HaschekSolutions/opentrashmail)
|
||||
[![](https://img.shields.io/github/stars/HaschekSolutions/opentrashmail.svg?label=Stars&style=social)](https://github.com/HaschekSolutions/opentrashmail)
|
||||
|
||||
#### Host your own `trashmail` solution to use with your own domains or subdomains
|
||||
|
||||
</div>
|
||||
|
||||
# Roadmap
|
||||
- [x] Mailserver
|
||||
- [x] Storing received mails in JSON
|
||||
- [x] Storing file attachments
|
||||
- [ ] Web interface
|
||||
- [ ] Choose email
|
||||
- [ ] Get random email address
|
||||
- [ ] Download attachments in a safe way
|
||||
- [ ] Customization options
|
||||
- [ ] Docker files and config
|
||||
|
||||
# Features
|
||||
- Python powered mail server that works out of the box for any domain
|
||||
|
|
|
@ -1,24 +1,118 @@
|
|||
import smtpd
|
||||
import asyncore
|
||||
import uuid
|
||||
import logging
|
||||
import email
|
||||
from email.header import decode_header
|
||||
from email.Utils import parseaddr
|
||||
import requests
|
||||
import time
|
||||
import os, sys
|
||||
import json
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
class CustomSMTPServer(smtpd.SMTPServer):
|
||||
def process_message(self, peer, mailfrom, rcpttos, data):
|
||||
print 'Receiving message from:', peer
|
||||
print 'Message addressed from:', mailfrom
|
||||
print 'Message addressed to :', rcpttos
|
||||
print 'Message length :', len(data)
|
||||
print "----------"
|
||||
for email in rcpttos:
|
||||
if not os.path.exists("../data/"+email):
|
||||
os.mkdir( "../data/"+email, 0755 )
|
||||
with open("../data/"+email+"/"+str(int(round(time.time() * 1000)))+".json", "w") as outfile:
|
||||
json.dump({'sender_ip':peer[0],'from':mailfrom,'rcpts':rcpttos,'data':data}, outfile)
|
||||
try:
|
||||
mailfrom = parseaddr(mailfrom)[1]
|
||||
logger.debug('Receiving message from: %s:%d' % peer)
|
||||
logger.debug('Message addressed from: %s' % mailfrom)
|
||||
logger.debug('Message addressed to: %s' % str(rcpttos))
|
||||
|
||||
|
||||
|
||||
msg = email.message_from_string(data)
|
||||
subject = ''
|
||||
for encoded_string, charset in decode_header(msg.get('Subject')):
|
||||
try:
|
||||
if charset is not None:
|
||||
subject += encoded_string.decode(charset)
|
||||
else:
|
||||
subject += encoded_string
|
||||
except:
|
||||
logger.exception('Error reading part of subject: %s charset %s' %
|
||||
(encoded_string, charset))
|
||||
|
||||
logger.debug('Subject: %s' % subject)
|
||||
|
||||
text_parts = []
|
||||
attachments = {}
|
||||
|
||||
#logger.debug('Headers: %s' % msg.items())
|
||||
|
||||
# YOU CAN DO SOME SECURITY CONTROLS HERE
|
||||
#if (not mailfrom.endswith("@hankenfeld.at") or
|
||||
# not msg.get('Mail-Header') == 'expected value'):
|
||||
# raise Exception("Email not trusted")
|
||||
|
||||
# loop on the email parts
|
||||
for part in msg.walk():
|
||||
if part.get_content_maintype() == 'multipart':
|
||||
continue
|
||||
|
||||
c_type = part.get_content_type()
|
||||
c_disp = part.get('Content-Disposition')
|
||||
|
||||
# text parts will be appended to text_parts
|
||||
if c_type == 'text/plain' and c_disp == None:
|
||||
text_parts.append(part.get_payload(decode=True).strip())
|
||||
# ignore html part
|
||||
elif c_type == 'text/html':
|
||||
continue
|
||||
# attachments will be sent as files in the POST request
|
||||
else:
|
||||
filename = part.get_filename()
|
||||
filecontent = part.get_payload(decode=True)
|
||||
if filecontent is not None:
|
||||
if filename is None:
|
||||
filename = 'untitled'
|
||||
attachments['file%d' % len(attachments)] = (filename,
|
||||
filecontent)
|
||||
|
||||
body = '\n'.join(text_parts)
|
||||
|
||||
except:
|
||||
logger.exception('Error reading incoming email')
|
||||
else:
|
||||
# this data will be sent as POST data
|
||||
edata = {
|
||||
'subject': subject,
|
||||
'body': body,
|
||||
'from': mailfrom,
|
||||
'attachments':[]
|
||||
}
|
||||
savedata = {'sender_ip':peer[0],'from':mailfrom,'rcpts':rcpttos,'raw':data,'parsed':edata}
|
||||
|
||||
filenamebase = str(int(round(time.time() * 1000)))
|
||||
|
||||
for em in rcpttos:
|
||||
if not os.path.exists("../data/"+em):
|
||||
os.mkdir( "../data/"+em, 0755 )
|
||||
|
||||
#same attachments if any
|
||||
for att in attachments:
|
||||
if not os.path.exists("../data/"+em+"/attachments"):
|
||||
os.mkdir( "../data/"+em+"/attachments", 0755 )
|
||||
attd = attachments[att]
|
||||
file = open("../data/"+em+"/attachments/"+filenamebase+"-"+attd[0], 'wb')
|
||||
file.write(attd[1])
|
||||
file.close()
|
||||
edata["attachments"].append(filenamebase+"-"+attd[0])
|
||||
|
||||
# save actual json data
|
||||
with open("../data/"+em+"/"+filenamebase+".json", "w") as outfile:
|
||||
json.dump(savedata, outfile)
|
||||
|
||||
#print edata
|
||||
return
|
||||
server = CustomSMTPServer(('0.0.0.0', 25), None)
|
||||
|
||||
asyncore.loop()
|
||||
if __name__ == '__main__':
|
||||
ch = logging.StreamHandler()
|
||||
ch.setLevel(logging.DEBUG)
|
||||
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
|
||||
ch.setFormatter(formatter)
|
||||
logger.setLevel(logging.DEBUG)
|
||||
logger.addHandler(ch)
|
||||
|
||||
server = CustomSMTPServer(('0.0.0.0', 2525), None) # use your public IP here
|
||||
asyncore.loop()
|
Loading…
Reference in a new issue