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

Benjamin Mako Hill || Want to submit a patch?