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

Benjamin Mako Hill || Want to submit a patch?