20e16c861ce13aaaa674783ea620adbcd88ab882
[attachcheck] / attachcheck
1 #!/usr/bin/env python
2
3 # AttachCheck -- A MTA wrapper to help check outgoing email for
4 # forgotten attachments.
5
6 # (c) 2004-2009 -- Benjamin Mako Hill <mako@atdot.cc>
7 # Software Homepage at: http://mako.cc/projects/attachcheck
8
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.
13
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.
18
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/>.
21
22 __copyright__ = "Copyright (c) 2004-2009 Benjamin Mako Hill"
23 __author__ = "Benjamin Mako Hill <mako@atdot.cc>" 
24
25 ### Configuration Options
26 ###########################################
27
28 # location of the sendmail binary
29 sendmail = "/usr/sbin/sendmail"
30
31 # list of mimetype which are, for the sake of this program, not
32 # attachments
33 ignored_types = ( "applica/pgp-signat", "application/pgp-signature" )
34
35 # list of regular expressions which we will view as being indicative
36 # of an attachment
37 attachment_regexes = [ r'\battach(ed|ment|ing)?\b(?im)',
38                        r'\balleg(o|at[oaie]|ando)' ]
39
40
41 ### No Edit Below This Line
42 ###########################################
43
44 import sys
45 import os
46 import email
47 import re
48
49 ## SUB: send message
50 def send_message(): 
51
52     global sendmail
53     sendmail = sendmail + " " + " ".join( sys.argv[1:] )
54    
55     mailpipe = os.popen("%s" % sendmail, 'w')
56     mailpipe.write( message_string )
57     sys.exit( mailpipe.close() )
58
59 ## SUB: print error message
60 def print_error():
61     print >>sys.stderr, \
62 """(Your message mentions attachments but does not include any.
63
64 Either:
65
66  (1) Add an attachment or add \"CONFIRM\" (in ALLCAPS to beginning of
67      the subject of the message and resend.  \"CONFIRM\" will be
68      stripped from your message subject befor the message is sent.
69
70
71  (2) Add a \"X-AttachCheck-Override: Yes\" header the message.
72
73 Read the documentation for AttachCheck for more details."""
74     
75 # get the mail from stdin
76 message_string = sys.stdin.read()
77 message = email.message_from_string( message_string )
78
79 attachment_expected = False
80 attachment_seen = False
81 text_seen = False
82
83 for part in message.walk():
84
85     # check to see if this part is a plain text entry
86     if part.get_content_type() == "text/plain":
87
88         # if this is the second text block, we should interrate
89         # through things
90         if text_seen:
91             attachment_seen = True
92         # otherwise, mark that we've now seen one text field
93         else:
94             text_seen = True
95
96         # search for the word "attach" or "attached" or "attachment"
97         # in the body of the message
98         for regex in attachment_regexes:
99
100             if re.search( regex, part.get_payload() ):
101                 attachment_expected = True
102
103     # check to see if this mime-type is something we can ignore
104     elif ( re.match( r'message/(?m)', part.get_content_type() ) or
105            re.match( r'multipart/(?m)', part.get_content_type() ) or
106            part.get_content_type() in ignored_types ):
107         continue
108
109     # if it's not text and it's an ignored type, it's a seen attachment
110     else:
111         attachment_seen = True
112
113 # now check to see if we are expecting an attachment
114 if attachment_expected:
115
116     # if we are expecting an attachment and we've seen one, we should
117     # send the file and be done with things
118     if attachment_seen:
119         send_message()
120
121     # if we are expected have not seen it, we need to check to see if
122     # the mail has been confirmed
123
124     else:
125         # check for the confirmation
126
127         if re.search( r'Subject: CONFIRM', message_string ):
128             message_string = re.sub( r'(Subject: )(CONFIRM)(.*?)\n',
129                                      r'\1\3\n', message_string )
130             send_message()
131
132         elif message.get( 'X-AttachCheck-Override' ) == "Yes":
133             send_message()
134             
135         else:
136             print_error()
137             sys.exit( 1 )
138
139
140 # if we are not expecting anything more, we should send the message
141 else:
142     send_message()

Benjamin Mako Hill || Want to submit a patch?