3 # AttachCheck -- A MTA wrapper to help check outgoing email for
4 # forgotten attachments.
6 # (c) 2004-2009 -- Benjamin Mako Hill <mako@atdot.cc>
7 # Software Homepage at: http://mako.cc/projects/attachcheck
9 # This program is free software: you can redistribute it and/or modify
10 # it under the terms of the GNU General Public License as published by
11 # the Free Software Foundation, either version 3 of the License, or (at
12 # your option) any later version.
14 # This program is distributed in the hope that it will be useful, but
15 # WITHOUT ANY WARRANTY; without even the implied warranty of
16 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 # General Public License for more details.
19 # You should have received a copy of the GNU General Public License
20 # along with this program. If not, see <http://www.gnu.org/licenses/>.
22 __copyright__ = "Copyright (c) 2004-2009 Benjamin Mako Hill"
23 __author__ = "Benjamin Mako Hill <mako@atdot.cc>"
25 ### Configuration Options
26 ###########################################
28 # location of the sendmail binary
29 sendmail = "/usr/sbin/sendmail"
31 # list of mimetype which are, for the sake of this program, not
33 ignored_types = ( "applica/pgp-signat", "application/pgp-signature" )
35 # list of regular expressions which we will view as being indicative
37 attachment_regexes = [ r'\battach(ed|ment|ing)?\b(?im)',
38 r'\balleg(o|at[oaie]|ando)(?im)' ]
40 # ignore quoted text (which might refer to attachments in previous emails)
41 attachment_regexes = [ r'(^|^[^\n>].*)' + x for x in attachment_regexes ]
43 ### No Edit Below This Line
44 ###########################################
52 def send_message(message):
56 # construct the sendmail pipe more safely (thanks iain murray!)
60 from subprocess import Popen, PIPE
61 process = Popen(cmd, stdin=PIPE)
62 mailpipe = process.stdin
63 mailpipe.write( message.as_string().encode() )
65 sys.exit( process.wait() )
67 ## SUB: print error message
70 """(Your message mentions attachments but does not include any.
74 (1) Add an attachment or add \"CONFIRM\" (in ALLCAPS to beginning of
75 the subject of the message and resend. \"CONFIRM\" will be
76 stripped from your message subject befor the message is sent.
79 (2) Add a \"X-AttachCheck-Override: Yes\" header the message.
81 Read the documentation for AttachCheck for more details.""", file=sys.stderr)
83 # get the mail from stdin
84 message_bytes = sys.stdin.buffer.read()
85 message = email.message_from_bytes( message_bytes )
87 attachment_expected = False
88 attachment_seen = False
91 for part in message.walk():
93 # check to see if this part is a plain text entry
94 if part.get_content_type() == "text/plain":
96 # if this is the second text block, we should interrate
99 attachment_seen = True
100 # otherwise, mark that we've now seen one text field
104 # search for the word "attach" or "attached" or "attachment"
105 # in the body of the message
106 for regex in attachment_regexes:
108 if re.search( regex, part.get_payload() ):
109 attachment_expected = True
111 # check to see if this mime-type is something we can ignore
112 elif ( re.match( r'message/(?m)', part.get_content_type() ) or
113 re.match( r'multipart/(?m)', part.get_content_type() ) or
114 part.get_content_type() in ignored_types ):
117 # if it's not text and it's an ignored type, it's a seen attachment
119 attachment_seen = True
121 # now check to see if we are expecting an attachment
122 if attachment_expected:
124 # if we are expecting an attachment and we've seen one, we should
125 # send the file and be done with things
127 send_message(message)
129 # if we are expected have not seen it, we need to check to see if
130 # the mail has been confirmed
133 # check for the confirmation
134 subject_string = message['Subject']
135 print(subject_string)
136 if re.search( r'^\s*CONFIRM', subject_string ):
137 subject_string = re.sub( r'(CONFIRM )(.*?)', r'\2', subject_string )
138 message['Subject'] = subject_string
139 send_message(message)
141 elif message['X-AttachCheck-Override'] == "Yes":
142 send_message(message)
149 # if we are not expecting anything more, we should send the message
151 send_message(message)