2 # Justin Quick 02-2008 justquick@gmail.com
3 # Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
4 # Everyone is permitted to copy and distribute verbatim copies
5 # of this license document, but changing it is not allowed. see LICENSE
6 import sys
,smtplib
,MimeWriter
,base64
,StringIO
,os
,mimetypes
,optparse
,getpass
,re
,socket
7 # ip,domain and dns helpers
8 def getdomain(hostname
): return '.'.join(hostname
.split('.')[1:])
10 return re
.compile('\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}').match(astr
)
12 # Returns highest priority server from mx search
13 capture
,servers
= 0,[]
14 for l
in os
.popen('dig %s mx'%domain
):
16 if l
==';; ANSWER SECTION:': capture
= 1
17 elif l
=='': capture
= 0
19 a
,b
= l
.split('\t')[-1].split(' ')
20 servers
.append((int(a
),b
[:-1]))
21 servers
.sort(lambda x
,y
: cmp(x
[0],y
[0]))
24 desc
= """MimeMail: Simple smtp client able to send mime messages which allows for: html body (from input or file), attachments, bcc/cc. It can also dig through a domain and retrieve the highest priority server from an mx search."""
25 parser
= optparse
.OptionParser(description
=desc
,
26 usage
='%prog [<username>@<domain>|<username> <servername>]')
27 parser
.add_option('-p',"--password", action
='store_true',dest
="passwd",
28 default
=False,help="authenticate user, prompting for password")
29 parser
.add_option("--key", dest
="key",default
='',help="the TLS keyfile")
30 parser
.add_option("--cert", dest
="cert",default
='',help="the TLS certfile")
31 (options
, args
) = parser
.parse_args()
32 # get username, smtp server hostname and domain for connection
33 if args
and args
[0].find('@')!=-1:
34 user
,domain
= args
[0].split('@')
35 server
= getmx(domain
)
36 elif args
and len(args
)==2:
38 if likeip(server
): server
= socket
.gethostbyaddr(server
)[0]
39 domain
= getdomain(server
)
41 print parser
.format_help()
44 try: smtp
= smtplib
.SMTP(server
)#; smtp.set_debuglevel(1)
45 except: raise 'Connection error with %s'%server
46 # login and check credentials
48 try: smtp
.login(user
,getpass
.getpass('Password for %s@%s: '%(user
,domain
)))
49 except: raise 'Credentials error with %s on %s'%(user
,server
)
50 # start Transport Layer Security mode if keyfile and certfile
51 if options
.key
and options
.cert
:
52 smtp
.starttls(options
.key
,options
.cert
)
54 # interface for writing MIME email messages
55 subject
,body
,to
,bcc
,cc
,attchs
= '','',[],[],[],[]
56 def prompt(prompt
): return raw_input(prompt
).strip()
59 global server
,port
,subject
,body
,to
,bcc
,cc
,attchs
63 print 'New HTML body content:\n'
66 try: data
= raw_input()
67 except EOFError: break
71 body
= open(prompt('Filename: ')).read()
72 elif c
==3: subject
= prompt('New subject: ')
73 elif c
==4: to
= prompt('Email addresses (comma sep): ').split(',')
75 fi
= prompt('Filename: ')
76 if not os
.path
.isfile(fi
):
77 raise IOError, 'File %s not found!'%fi
80 bcc
= prompt('BCCs (comma sep): ').split(',')
81 cc
= prompt('CCs (comma sep): ').split(',')
84 print """Connected. Now compose message:
86 \t2. Load message body from file
93 try: cmd
= prompt("smtp> ")
94 except: print '\nExiting'; sys
.exit()
96 # Init mime message with necessary headers
97 message
= StringIO
.StringIO()
98 writer
= MimeWriter
.MimeWriter(message
)
99 writer
.addheader('Subject', subject
)
100 for addr
in to
: writer
.addheader('To', addr
)
101 for addr
in cc
: writer
.addheader('Cc', addr
)
102 for addr
in bcc
: writer
.addheader('Bcc', addr
)
103 writer
.startmultipartbody('mixed')
105 part
= writer
.nextpart()
106 bod
= part
.startbody('text/html')
110 part
= writer
.nextpart()
111 part
.addheader('Content-Transfer-Encoding', 'base64')
112 part
.addheader("Content-Disposition",
113 'attachment; filename="%s"'%os.path
.basename(attch
))
114 mime
= mimetypes
.guess_type(attch
)[0]
115 if not mime
: mime
= 'text/plain'
116 base64
.encode(open(attch
,'rb'),part
.startbody(mime
))
119 errors
= smtp
.sendmail('%s@%s'%(user
,domain
), to
, message
.getvalue())
121 if not errors
: print 'Email sent sucessfully'
123 for k
,v
in errors
.items(): print 'Error with address %s: %s'%(k
,v
)
124 # close connection before exiting