]> code.delx.au - monosys/blob - bin/sendmail.py
pacorphan improvements
[monosys] / bin / sendmail.py
1 #!/usr/bin/env python
2
3 """
4 sendmailish python program.
5 Usage: sendmail.py toaddress
6
7
8 Sample config file
9 ------------------
10 # vim:ft=python
11
12 smtpServers = [
13 SMTPProxy(remoteServer='mail.internode.on.net', domainSuffix='.internode.on.net'),
14 SMTPProxy(remoteServer='smtp.usyd.edu.au', domainSuffix='.usyd.edu.au'),
15 SMTPProxy(remoteServer='mail.iinet.net.au', domainSuffix='.iinet.net.au'),
16 SMTPProxy(remoteServer='mail.netspace.net.au', domainSuffix='.netspace.net.au'),
17 SMTPProxy(remoteServer='mail.optusnet.com.au', domainSuffix='.optusnet.com.au'),
18 SMTPProxySSH(remoteServer='delx.net.au', remoteSendmail='/usr/sbin/sendmail'),
19 ]
20
21 myIPURL = "http://suits.ug.it.usyd.edu.au/myip.php"
22 """
23
24
25
26 import smtplib, email, urllib
27 import os.path, subprocess, sys, optparse
28 import logging
29
30 try:
31 # Attempt to load this debugging decorator function
32 from decorators import logCall
33 except ImportError:
34 def logCall(f):
35 '''This is a no-op decorator function'''
36 return f
37
38
39 class SMTPProxyBase(object):
40 def __repr__(self):
41 return '%s(%s)' % (self.__class__.__name__,
42 ', '.join('%s=%r' % (k, getattr(self, k)) for k in self.__slots__)
43 )
44
45 class SMTPProxy(SMTPProxyBase):
46 __slots__ = (
47 'remoteServer',
48 'domainSuffix',
49 'username',
50 'password',
51 'useSSL',
52 )
53 @logCall
54 def __init__(self, remoteServer, domainSuffix, username=None, password=None, useSSL=False):
55 self.remoteServer = remoteServer
56 self.domainSuffix = domainSuffix
57
58 self.username = username
59 self.password = password
60 self.useSSL = useSSL
61
62 def doesHandle(self, localhostName):
63 '''Determines if this SMTPProxy can be used within this domain'''
64 if localhostName is None:
65 return False
66 else:
67 return localhostName.endswith(self.domainSuffix)
68
69 def sendmail(self, fromAddr, toAddrs, message):
70 '''
71 Actually send the mail.
72
73 Returns true if the mail was successfully send
74 '''
75
76 smtp = smtplib.SMTP(self.remoteServer)
77 if self.useSSL:
78 smtp.starttls()
79 if self.username is not None and self.password is not None:
80 smtp.login(self.username, self.password)
81 smtp.sendmail(fromAddr, toAddrs, message)
82 smtp.quit()
83 return True
84
85 class SMTPProxySSH(SMTPProxyBase):
86 __slots__ = ('remoteServer',)
87 @logCall
88 def __init__(self, remoteServer, remoteSendmail):
89 self.remoteServer = remoteServer
90 self.remoteSendmail = remoteSendmail
91
92 def doesHandle(self, *args, **kwargs):
93 '''
94 Determines if this SMTPProxySSH can be used within this domain.
95 Note: This method returns true for all values.
96 '''
97 return True
98
99 def sendmail(self, fromAddr, toAddrs, message):
100 '''
101 Actually send the mail.
102
103 Returns true if the mail was successfully send
104 '''
105 cmdline = ['ssh', self.remoteServer, self.remoteSendmail, '--']
106 cmdline.extend(toAddrs)
107 process = subprocess.Popen(cmdline, stdin=subprocess.PIPE)
108 process.communicate(message)
109 return not bool(process.wait())
110
111 def getOptionParser():
112 parser = optparse.OptionParser(usage="%prog [options] toAddress1 [toAddress2] ...")
113 parser.add_option('--debug',
114 action='store_const', dest='debugLevel', const=logging.DEBUG,
115 help='Sets the logging level to debug')
116 parser.add_option('--warn',
117 action='store_const', dest='debugLevel', const=logging.WARNING,
118 help='Sets the logging level to warn')
119 parser.set_default('debugLevel', logging.ERROR)
120
121 return parser
122
123 def main():
124 # Load the config file
125 try:
126 exec(open(os.path.expanduser('~/.sendmailpyrc'), 'r').read())
127 except Exception, e:
128 print >>sys.stderr, 'Error with config file:', e
129 return False
130
131 # Get the to addresses
132 parser = getOptionParser()
133 options, toAddrs = parser.parse_args()
134 logging.basicConfig(level=options.debugLevel)
135 if not toAddrs:
136 parser.error('No to addresses found')
137
138 # Pick a SMTP server
139 try:
140 host = urllib.urlopen(myIPURL).read().strip()
141 except:
142 host = None
143 logging.exception('Failed to grab our external domain name')
144
145 for smtpProxy in smtpServers:
146 if smtpProxy.doesHandle(host):
147 # Got the correct smtpServer
148 logging.info('Using the Proxy %r to connect from %s', smtpProxy, host)
149 break
150 else:
151 logging.error('Did not find a proxy to connect from %s', host)
152 return False
153
154 # Get the from address
155 message = sys.stdin.read()
156 fromAddr = email.message_from_string(message)["from"]
157 _, fromAddr = email.utils.parseaddr(fromAddr)
158
159 return smtpProxy.sendmail(fromAddr, toAddrs, message)
160
161 if __name__ == "__main__":
162 # Specify SMTP servers here
163 sys.exit(not main())
164