noted the new URL for git repository
[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)(?im)' ]
39
40 # ignore quoted text (which might refer to attachments in previous emails)
41 attachment_regexes = [ r'(^|^[^\n>].*)' + x for x in attachment_regexes ]
42
43 ### No Edit Below This Line
44 ###########################################
45
46 import sys
47 import os
48 import email
49 import re
50
51 ## SUB: send message
52 def send_message(): 
53
54     global sendmail
55
56     # construct the sendmail pipe more safely (thanks iain murray!)
57     cmd = sys.argv[:]
58     cmd[0] = sendmail
59
60     from subprocess import Popen, PIPE
61     mailpipe = Popen(cmd, stdin=PIPE).stdin
62     mailpipe.write( message_string )
63     sys.exit( mailpipe.close() )
64
65 ## SUB: print error message
66 def print_error():
67     print >>sys.stderr, \
68 """(Your message mentions attachments but does not include any.
69
70 Either:
71
72  (1) Add an attachment or add \"CONFIRM\" (in ALLCAPS to beginning of
73      the subject of the message and resend.  \"CONFIRM\" will be
74      stripped from your message subject befor the message is sent.
75
76
77  (2) Add a \"X-AttachCheck-Override: Yes\" header the message.
78
79 Read the documentation for AttachCheck for more details."""
80     
81 # get the mail from stdin
82 message_string = sys.stdin.read()
83 message = email.message_from_string( message_string )
84
85 attachment_expected = False
86 attachment_seen = False
87 text_seen = False
88
89 for part in message.walk():
90
91     # check to see if this part is a plain text entry
92     if part.get_content_type() == "text/plain":
93
94         # if this is the second text block, we should interrate
95         # through things
96         if text_seen:
97             attachment_seen = True
98         # otherwise, mark that we've now seen one text field
99         else:
100             text_seen = True
101
102         # search for the word "attach" or "attached" or "attachment"
103         # in the body of the message
104         for regex in attachment_regexes:
105
106             if re.search( regex, part.get_payload() ):
107                 attachment_expected = True
108
109     # check to see if this mime-type is something we can ignore
110     elif ( re.match( r'message/(?m)', part.get_content_type() ) or
111            re.match( r'multipart/(?m)', part.get_content_type() ) or
112            part.get_content_type() in ignored_types ):
113         continue
114
115     # if it's not text and it's an ignored type, it's a seen attachment
116     else:
117         attachment_seen = True
118
119 # now check to see if we are expecting an attachment
120 if attachment_expected:
121
122     # if we are expecting an attachment and we've seen one, we should
123     # send the file and be done with things
124     if attachment_seen:
125         send_message()
126
127     # if we are expected have not seen it, we need to check to see if
128     # the mail has been confirmed
129
130     else:
131         # check for the confirmation
132
133         if re.search( r'Subject: CONFIRM', message_string ):
134             message_string = re.sub( r'(Subject: )(CONFIRM )(.*?)\n',
135                                      r'\1\3\n', message_string )
136             send_message()
137
138         elif message.get( 'X-AttachCheck-Override' ) == "Yes":
139             send_message()
140             
141         else:
142             print_error()
143             sys.exit( 1 )
144
145
146 # if we are not expecting anything more, we should send the message
147 else:
148     send_message()

Benjamin Mako Hill || Want to submit a patch?