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

Benjamin Mako Hill || Want to submit a patch?