-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathfwdmail.py
executable file
·157 lines (139 loc) · 4.77 KB
/
fwdmail.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
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
#!/usr/bin/env python3
"""
This script forwards your local UNIX mail to a given remote address using the
parameters you specify. You can accomplish the same thing with a local MTA, but
you will almost certainly run into difficulties if your machine is not
externally-facing and does not have an FQDN.
Getting local mail working at all seems to be a fiddly process. A
non-exhaustive list of things I often need to do is:
- Configure postfix (or whatever mail server you're using) to deliver local
mail. sudo dpkg-reconfigure postfix if necessary.
- sudo usermod -aG mail ${USER}
- sudo touch /var/mail/${USER} && sudo chown ${USER} /var/mail/${USER}
All code in this script is in the public domain. You may use it for any purpose
(including commercial) with or without attribution to the original author.
There are some current limitations including authentication support only up to
TLS and no support for local mailboxes in a format other than mbox. If you need
this changed or would like some other feature implemented ping me and I'll
probably do it.
Matthew Fernandez <[email protected]>
"""
import argparse
import contextlib
import functools
import getpass
import grp
import itertools
import mailbox
import os
import smtplib
import socket
import sys
import syslog
import time
@contextlib.contextmanager
def locked(mailbox):
try:
yield mailbox
finally:
mailbox.unlock()
def main(argv, stdout, stderr):
# Parse command line arguments.
parser = argparse.ArgumentParser(prog='fwdmail.py',
description='Forward local mail to another address',
fromfile_prefix_chars='@')
parser.add_argument('--check_connection', action='store_true',
help='Exit with success if offline')
parser.add_argument('--from_address', '-f', required=True,
help='From address to use when forwarding')
parser.add_argument('--login',
help='Login name if authentication is required for sending')
parser.add_argument('--mbox', '-m', default='/var/mail/%s' % getpass.getuser(),
help='Mbox file to open if not the default')
parser.add_argument('--port', '-p', default=25, type=int,
help='Port to send through')
parser.add_argument('--password',
help='Password if authentication is required for sending')
parser.add_argument('--server', '-s', required=True,
help='SMTP server to forward messages through')
parser.add_argument('--to', '-t', required=True,
help='Address to forward to')
parser.add_argument('--tls', action='store_true',
help='Use TLS security')
p = parser.parse_args(argv[1:])
hostname = socket.gethostname()
# Check we're part of expected groups.
for gid in os.getgroups():
if grp.getgrgid(gid).gr_name == 'mail':
break
else:
stderr('Warning: you are not part of the \'mail\' group')
# Open the local mailbox.
box = None
try:
box = mailbox.mbox(p.mbox)
except Exception as inst:
stderr(f'Failed to open {p.mbox}: {inst}')
return 1
smtp = None
# Forward and delete each message.
try:
box.lock()
except Exception as inst:
stderr(f'Failed to lock mailbox file: {inst}')
return -1
with locked(box) as b:
for i in itertools.count():
try:
key, msg = b.popitem()
except KeyError:
# mailbox empty
break
if not smtp:
# Connect to the SMTP server.
try:
try:
smtp = smtplib.SMTP(p.server, p.port)
smtp.connect(p.server, p.port)
except Exception as inst:
if p.check_connection:
return 0
else:
raise inst
if p.tls:
smtp.starttls()
if p.login:
smtp.login(p.login, p.password)
except Exception as inst:
stderr(f'Failed to connect to {p.server}: {inst}')
return 1
try:
smtp.sendmail(p.from_address, p.to,
(f'From: {p.from_address}\r\nTo: {p.to}\r\nSubject: {hostname}: '
f'{msg["Subject"] or ""}\r\n\r\nForwarded email from {hostname}:'
f'{p.mbox}:\r\n\r\n{msg or ""}').encode('utf-8', 'replace'))
b.flush()
except Exception as inst:
stderr(f'Failed to send/delete message {key}: {inst}')
try:
smtp.quit()
except:
pass # Ignore.
return -1
# pause every 10 emails to avoid flooding the SMTP server
if i % 10 == 9:
time.sleep(0.5)
if smtp: smtp.quit()
return 0
if __name__ == '__main__':
if sys.stdout.isatty():
def stdout(msg):
sys.stdout.write(f'{msg}\n')
def stderr(msg):
sys.stderr.write(f'{msg}\n')
else:
# We seem to be running from a crontab.
syslog.openlog('fwdmail', syslog.LOG_PID, syslog.LOG_MAIL)
stdout = syslog.syslog
stderr = functools.partial(syslog.syslog, syslog.LOG_ERR)
sys.exit(main(sys.argv, stdout, stderr))