3 # fakemail2 - rewrite of Perl's fakemail in python
5 # Author: Thomas Guyot-Sionnest <tguyot@gmail.com>
7 # This file has been released to the public domain
10 from __future__
import print_function
13 from optparse
import OptionParser
14 #from tempfile import TemporaryFile
15 from smtpd
import SMTPServer
16 from asyncore
import loop
17 from time
import ctime
22 class FakemailSMTPD(SMTPServer
):
23 def __init__(self
, localaddr
, remoteaddr
, saveobj
, logobj
):
24 self
.capture
= saveobj
26 SMTPServer
.__init
__(self
, localaddr
, remoteaddr
)
28 def process_message(self
, peer
, mailfrom
, rcptto
, data
):
29 self
.message
.log('Incoming mail')
31 self
.message
.log('Capturing mail to %s' % recp
)
32 self
.capture
.save(mailfrom
, recp
, data
)
33 self
.message
.log('Mail to %s saved' % recp
)
34 self
.message
.log('Incoming mail dispatched')
37 def __init__(self
, logfile
=None):
39 self
.logfile
= logfile
+ os
.getpid()
41 with
open(self
.logfile
, 'a'):
46 def log(self
, message
):
47 msg
= ctime() + ': ' + str(message
)
49 with
open(self
.logfile
, 'a') as log
:
55 def __init__(self
, savepath
):
56 if not os
.path
.isdir(savepath
):
57 raise Exception('Savepath is not a directory: %s' % savepath
)
58 # TODO: Test-write, fail early on EACCESS
62 def save(self
, sender
, recipient
, data
):
63 msgfile
= os
.path
.join(self
.path
, self
.recp2file(recipient
))
64 with
open(msgfile
, 'w') as outf
:
65 outf
.write('MAIL FROM: <%s>\nRCPT TO: <%s>\nDATA:\n%s\n' % (sender
, recipient
, data
))
67 def recp2file(self
, recipient
):
68 for char
in r
'|<>&/\ ;!?':
69 recipient
= recipient
.replace(char
, '')
70 self
.counts
[recipient
] = self
.counts
.get(recipient
, 0) + 1
71 return '.'.join((recipient
, str(self
.counts
[recipient
])))
74 process_opts
= OptionParser(
75 usage
='Usage: %prog -H <hostname> -p <port> -P <log_path> [-l <log_file>] [-b]',
76 version
='%prog ' + VERSION
78 process_opts
.add_option('-H', '--host', dest
='host', help='Hostname/IP to listen on')
79 process_opts
.add_option('-p', '--port', type='int', dest
='port', help='Port to listen on')
80 process_opts
.add_option('-P', '--path', dest
='path', help='Directory to save emails into')
81 process_opts
.add_option('-l', '--log', dest
='log', help='Optionnal file to append messages to')
82 process_opts
.add_option('-b', '--background', action
='store_true', dest
='background', help='Fork to background')
84 (options
, args
) = process_opts
.parse_args()
85 if not options
.host
or not options
.port
or not options
.path
:
86 process_opts
.error('You must supply a host, port and path')
87 if not os
.path
.isdir(options
.path
):
88 process_opts
.error('Path \'%s\' is not a directory' % options
.path
)
90 process_opts
.error('Unexpected extra argument: %s' % args
[0])
92 # Fork to background if requested
93 if options
.background
:
97 except OSError as err
:
98 print('Fork failed: %s (%d)' % (err
.strerror
, err
.errno
), file=sys
.stderr
)
102 for fdno
in range(0, 3):
114 message
= LogIt(options
.log
)
115 capture
= SaveIt(options
.path
)
118 message
.log('Starting fakemail')
119 FakemailSMTPD((options
.host
, options
.port
), None, capture
, message
)
120 loop(timeout
=1, use_poll
=True)
121 message
.log('Shutting down')
123 if __name__
== '__main__':