Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Newer
Older
100644 387 lines (362 sloc) 14.829 kb
1d4dbf4 1) Made the comments more rdoc-friendly
dagbrown authored
1 #!/opt/bin/ruby
a66ee25 @dagbrown Added magic encoding strings
authored
2 # -*- encoding : utf-8 -*-
e55fb7a Adding the gurgitate-mail files to rubyforge so that others may find …
dagbrown authored
3 #------------------------------------------------------------------------
4 # Mail filter package
5 #------------------------------------------------------------------------
6
7 require 'etc'
8
d7aa8f8 Er.
dagbrown authored
9 require 'gurgitate/mailmessage'
10 require 'gurgitate/deliver'
e55fb7a Adding the gurgitate-mail files to rubyforge so that others may find …
dagbrown authored
11
d7aa8f8 Er.
dagbrown authored
12 module Gurgitate
618301b @dagbrown Added a bunch of documentation to gurgitate-mail.RB
authored
13 # This is the actual gurgitator; it reads a message and then it can
14 # do other stuff with it, like saving it to a mailbox or forwarding
1d4dbf4 1) Made the comments more rdoc-friendly
dagbrown authored
15 # it somewhere else.
618301b @dagbrown Added a bunch of documentation to gurgitate-mail.RB
authored
16 #
17 # To set configuration parameters for gurgitate-mail, use a keyword-
18 # based system. It's almost like an attribute, only if you give the
19 # accessor a parameter, it will set the configuration parameter to
20 # the parameter's value. For instance:
bdbfd6a @dagbrown Merging a couple of branches together, a file at a time.
authored
21 #
618301b @dagbrown Added a bunch of documentation to gurgitate-mail.RB
authored
22 # maildir "#{homedir}/Mail"
23 # sendmail "/usr/sbin/sendmail"
24 # spoolfile "Maildir"
25 # spooldir homedir
26 #
27 # (This is because of an oddity in Ruby where, even if an
28 # accessor exists in the current object, if you say:
29 # name = value
30 # it'll always create a local variable. Not quite what you
31 # want when you're trying to set a config parameter. You have
32 # to say <code>self.name = value</code>, which [I think] is ugly.
33 #
bdbfd6a @dagbrown Merging a couple of branches together, a file at a time.
authored
34 # In the interests of promoting harmony, of course,
618301b @dagbrown Added a bunch of documentation to gurgitate-mail.RB
authored
35 # <code>self.name = value</code> still works.)
36 #
37 # The attributes you can define are:
38 #
39 # homedir :: Your home directory. This defaults to what your
40 # actual home directory is.
41 #
42 # maildir :: The directory you store your mail in. This defaults
43 # to the "Mail" directory in your home dir.
44 #
45 # logfile :: The path to your gurgitate-mail log file. If you
46 # set this to +nil+, gurgitate-mail won't log anything.
47 # The default value is ".gurgitate.log" in your home
48 # directory.
49 #
50 # The following parameters are more likely to be interesting to the
51 # system administrator than the everyday user.
52 #
53 # sendmail :: The full path of your "sendmail" program, or at least
54 # a program that provides functionality equivalent to
55 # sendmail.
56 #
57 # spoolfile :: The default location to store mail messages, for the
58 # messages that have been unaffected by your gurgitate
59 # rules. If an exception is raised by your rules, the
60 # message will be delivered to the spoolfile.
61 #
62 # spooldir :: The location where users' system mail boxes live.
63 #
64 # folderstyle :: The style of mailbox to create (and to expect,
bdbfd6a @dagbrown Merging a couple of branches together, a file at a time.
authored
65 # although gurgitate-mail automatically detects the
618301b @dagbrown Added a bunch of documentation to gurgitate-mail.RB
authored
66 # type of existing mailboxes). See the separate
67 # documentation for folderstyle for more details.
44ad9f4 Added more rdoc-friendly comments, and then buried the lot into a mod…
dagbrown authored
68 class Gurgitate < Mailmessage
50cf942 Okay, now I got that maildir delivery stuff working (thanks to the nice
dagbrown authored
69 include Deliver
70
6f0e207 Well! 1.7.0 is now the current version.
dagbrown authored
71 # Instead of the usual attributes, I went with a
72 # reader-is-writer type thing (as seen quite often in Perl and
73 # C++ code) so that in your .gurgitate-rules, you can say
74 #
75 # maildir "#{homedir}/Mail"
76 # sendmail "/usr/sbin/sendmail"
77 # spoolfile "Maildir"
78 # spooldir homedir
79 #
80 # This is because of an oddity in Ruby where, even if an
81 # accessor exists in the current object, if you say:
82 # name = value
83 # it'll always create a local variable. Not quite what you
84 # want when you're trying to set a config parameter. You have
85 # to say "self.name = value", which (I think) is ugly.
86 #
87 # In the interests of promoting harmony, of course, the previous
88 # syntax will continue to work.
89 def self.attr_configparam(*syms)
6fe05a1 Well! 1.7.0 is now the current version.
dagbrown authored
90 syms.each do |sym|
6f0e207 Well! 1.7.0 is now the current version.
dagbrown authored
91 class_eval %{
6fe05a1 Well! 1.7.0 is now the current version.
dagbrown authored
92 def #{sym} (*vals)
93 if vals.length == 1
94 @#{sym} = vals[0]
95 elsif vals.length == 0
96 @#{sym}
97 else
98 raise ArgumentError,
99 "wrong number of arguments " +
100 "(\#{vals.length} for 0 or 1)"
101 end
102 end
103
104 # Don't break it for the nice people who use
105 # old-style accessors though. Breaking people's
106 # .gurgitate-rules is a bad idea.
107 attr_writer :#{sym}
6f0e207 Well! 1.7.0 is now the current version.
dagbrown authored
108 }
6fe05a1 Well! 1.7.0 is now the current version.
dagbrown authored
109 end
110 end
111
44ad9f4 Added more rdoc-friendly comments, and then buried the lot into a mod…
dagbrown authored
112 # The directory you want to put mail folders into
6fe05a1 Well! 1.7.0 is now the current version.
dagbrown authored
113 attr_configparam :maildir
114
44ad9f4 Added more rdoc-friendly comments, and then buried the lot into a mod…
dagbrown authored
115 # The path to your log file
6fe05a1 Well! 1.7.0 is now the current version.
dagbrown authored
116 attr_configparam :logfile
117
44ad9f4 Added more rdoc-friendly comments, and then buried the lot into a mod…
dagbrown authored
118 # The full path of your "sendmail" program
6fe05a1 Well! 1.7.0 is now the current version.
dagbrown authored
119 attr_configparam :sendmail
120
44ad9f4 Added more rdoc-friendly comments, and then buried the lot into a mod…
dagbrown authored
121 # Your home directory
6fe05a1 Well! 1.7.0 is now the current version.
dagbrown authored
122 attr_configparam :homedir
123
44ad9f4 Added more rdoc-friendly comments, and then buried the lot into a mod…
dagbrown authored
124 # Your default mail spool
6fe05a1 Well! 1.7.0 is now the current version.
dagbrown authored
125 attr_configparam :spoolfile
126
44ad9f4 Added more rdoc-friendly comments, and then buried the lot into a mod…
dagbrown authored
127 # The directory where user mail spools live
6fe05a1 Well! 1.7.0 is now the current version.
dagbrown authored
128 attr_configparam :spooldir
129
5a65662 - Added a thing to the "filter" method so that you can say something …
dagbrown authored
130 # What kind of mailboxes you prefer
6fe05a1 Well! 1.7.0 is now the current version.
dagbrown authored
131 # attr_configparam :folderstyle
132
618301b @dagbrown Added a bunch of documentation to gurgitate-mail.RB
authored
133 # What kind of mailboxes you prefer. Treat this like a
bdbfd6a @dagbrown Merging a couple of branches together, a file at a time.
authored
134 # configuration parameter. If no argument is given, then
618301b @dagbrown Added a bunch of documentation to gurgitate-mail.RB
authored
135 # return the current default type.
136 #
137 # Depending on what you set this to, some other configuration
138 # parameters change. You can set this to the following things:
139 #
140 # <code>Maildir</code> :: Create Maildir mailboxes.
141 #
142 # This sets +spooldir+ to your home
143 # directory, +spoolfile+ to
bdbfd6a @dagbrown Merging a couple of branches together, a file at a time.
authored
144 # $HOME/Maildir and creates
618301b @dagbrown Added a bunch of documentation to gurgitate-mail.RB
authored
145 # mail folders underneath that.
146 #
147 # <code>MH</code> :: Create MH mail boxes.
148 #
149 # This reads your <code>.mh_profile</code>
150 # file to find out where you've told MH to
151 # find its mail folders, and uses that value.
152 # If it can't find that in your .mh_profile,
153 # it will assume you want mailboxes in
bdbfd6a @dagbrown Merging a couple of branches together, a file at a time.
authored
154 # $HOME/Mail. It sets +spoolfile+ to
618301b @dagbrown Added a bunch of documentation to gurgitate-mail.RB
authored
155 # "inbox" in your mail directory.
156 #
157 # <code>Mbox</code> :: Create +mbox+ mailboxes.
158 #
159 # This sets +spooldir+ to
160 # <code>/var/spool/mail</code> and
161 # +spoolfile+ to a file with your username
162 # in <code>/var/spool/mail</code>.
bdbfd6a @dagbrown Merging a couple of branches together, a file at a time.
authored
163 def folderstyle(*style)
6fe05a1 Well! 1.7.0 is now the current version.
dagbrown authored
164 if style.length == 0 then
aaf8976 Merging changes from 1.8 release branch
dagbrown authored
165 @folderstyle
6fe05a1 Well! 1.7.0 is now the current version.
dagbrown authored
166 elsif style.length == 1 then
167 if style[0] == Maildir then
168 spooldir homedir
169 spoolfile File.join(spooldir,"Maildir")
09545aa Merging in changes from the VERSION_1_8 branch.
dagbrown authored
170 maildir spoolfile
da90d29 @dagbrown All of the changes from 1.10 in one big lump.
authored
171 elsif style[0] == MH then
172 mh_profile_path = File.join(ENV["HOME"],".mh_profile")
173 if File.exists?(mh_profile_path) then
174 mh_profile = YAML.load(File.read(mh_profile_path))
175 maildir mh_profile["Path"]
176 else
177 maildir File.join(ENV["HOME"],"Mail")
178 end
179 spoolfile File.join(maildir,"inbox")
6fe05a1 Well! 1.7.0 is now the current version.
dagbrown authored
180 else
181 spooldir "/var/spool/mail"
182 spoolfile File.join(spooldir, @passwd.name)
183 end
bdbfd6a @dagbrown Merging a couple of branches together, a file at a time.
authored
184
aaf8976 Merging changes from 1.8 release branch
dagbrown authored
185 @folderstyle = style[0]
6fe05a1 Well! 1.7.0 is now the current version.
dagbrown authored
186 else
187 raise ArgumentError, "wrong number of arguments "+
188 "(#{style.length} for 0 or 1)"
189 end
aaf8976 Merging changes from 1.8 release branch
dagbrown authored
190 @folderstyle
6fe05a1 Well! 1.7.0 is now the current version.
dagbrown authored
191 end
44ad9f4 Added more rdoc-friendly comments, and then buried the lot into a mod…
dagbrown authored
192
193 # Set config params to defaults, read in mail message from
1d4dbf4 1) Made the comments more rdoc-friendly
dagbrown authored
194 # +input+
195 # input::
196 # Either the text of the email message in RFC-822 format,
197 # or a filehandle where the email message can be read from
aaf8976 Merging changes from 1.8 release branch
dagbrown authored
198 # recipient::
199 # The contents of the envelope recipient parameter
200 # sender::
201 # The envelope sender parameter
1d4dbf4 1) Made the comments more rdoc-friendly
dagbrown authored
202 # spooldir::
203 # The location of the mail spools directory.
846fe71 Merging in all those changes from 1.7, the better to launch the 1.8 b…
dagbrown authored
204 def initialize(input=nil,
205 recipient=nil,
206 sender=nil,
207 spooldir="/var/spool/mail",
208 &block)
5a65662 - Added a thing to the "filter" method so that you can say something …
dagbrown authored
209 @passwd = Etc.getpwuid
210 @homedir = @passwd.dir;
211 @maildir = File.join(@passwd.dir,"Mail")
212 @logfile = File.join(@passwd.dir,".gurgitate.log")
213 @sendmail = "/usr/lib/sendmail"
214 @spooldir = spooldir
215 @spoolfile = File.join(@spooldir,@passwd.name )
216 @folderstyle = MBox
969ab41 Merged in Aria's changes to handle systemwide configuration.
dagbrown authored
217 @rules = []
e32b2d3 - Made it deal correctly with headers that have tabs between the colon
dagbrown authored
218
89f4445 Incorporating a change suggested by Peter Shirley, to use the EUID to…
dagbrown authored
219 input_text = ""
84903a7 Changed a couple of small things for Ruby 1.9 compatibility:
dagbrown authored
220 input.each_line do |l| input_text << l end
aaf8976 Merging changes from 1.8 release branch
dagbrown authored
221 super(input_text, recipient, sender)
1d4dbf4 1) Made the comments more rdoc-friendly
dagbrown authored
222 instance_eval(&block) if block_given?
e55fb7a Adding the gurgitate-mail files to rubyforge so that others may find …
dagbrown authored
223 end
224
618301b @dagbrown Added a bunch of documentation to gurgitate-mail.RB
authored
225 def add_rules(filename, options = {}) #:nodoc:
969ab41 Merged in Aria's changes to handle systemwide configuration.
dagbrown authored
226 if not Hash === options
227 raise ArgumentError.new("Expected hash of options")
228 end
229 if filename == :default
230 filename=homedir+"/.gurgitate-rules"
231 end
232 if not FileTest.exist?(filename)
233 filename = filename + '.rb'
234 end
e170a4a - Improved permissions error logging
aredridel authored
235 if not FileTest.exist?(filename)
6fe05a1 Well! 1.7.0 is now the current version.
dagbrown authored
236 if options.has_key?(:user)
237 log("#{filename} does not exist.")
238 end
e170a4a - Improved permissions error logging
aredridel authored
239 return false
240 end
241 if FileTest.file?(filename) and
969ab41 Merged in Aria's changes to handle systemwide configuration.
dagbrown authored
242 ( ( not options.has_key? :system and
243 FileTest.owned?(filename) ) or
244 ( options.has_key? :system and
245 options[:system] == true and
246 File.stat(filename).uid == 0 ) ) and
247 FileTest.readable?(filename)
248 @rules << filename
249 else
e170a4a - Improved permissions error logging
aredridel authored
250 log("#{filename} has bad permissions or ownership, not using rules")
251 return false
969ab41 Merged in Aria's changes to handle systemwide configuration.
dagbrown authored
252 end
253 end
254
618301b @dagbrown Added a bunch of documentation to gurgitate-mail.RB
authored
255 # Deletes (discards) the current message.
44ad9f4 Added more rdoc-friendly comments, and then buried the lot into a mod…
dagbrown authored
256 def delete
1d4dbf4 1) Made the comments more rdoc-friendly
dagbrown authored
257 # Well, nothing here, really.
e55fb7a Adding the gurgitate-mail files to rubyforge so that others may find …
dagbrown authored
258 end
259
618301b @dagbrown Added a bunch of documentation to gurgitate-mail.RB
authored
260 # This is kind of neat. You can get a header by calling its
261 # name as a method. For example, if you want the header
262 # "X-Face", then you call x_face and that gets it for you. It
263 # raises NameError if that header isn't found.
264 #
5a65662 - Added a thing to the "filter" method so that you can say something …
dagbrown authored
265 # meth::
266 # The method that the caller tried to call which isn't
267 # handled any other way.
268 def method_missing(meth)
269 headername=meth.to_s.split(/_/).map {|x| x.capitalize}.join("-")
84903a7 Changed a couple of small things for Ruby 1.9 compatibility:
dagbrown authored
270 if headers[headername] then
5a65662 - Added a thing to the "filter" method so that you can say something …
dagbrown authored
271 return headers[headername]
272 else
273 raise NameError,"undefined local variable or method, or header not found `#{meth}' for #{self}:#{self.class}"
274 end
275 end
276
1d4dbf4 1) Made the comments more rdoc-friendly
dagbrown authored
277 # Forwards the message to +address+.
618301b @dagbrown Added a bunch of documentation to gurgitate-mail.RB
authored
278 #
1d4dbf4 1) Made the comments more rdoc-friendly
dagbrown authored
279 # address::
280 # A valid email address to forward the message to.
44ad9f4 Added more rdoc-friendly comments, and then buried the lot into a mod…
dagbrown authored
281 def forward(address)
282 self.log "Forwarding to "+address
283 IO.popen(@sendmail+" "+address,"w") do |f|
284 f.print(self.to_s)
e55fb7a Adding the gurgitate-mail files to rubyforge so that others may find …
dagbrown authored
285 end
286 end
287
1d4dbf4 1) Made the comments more rdoc-friendly
dagbrown authored
288 # Writes +message+ to the log file.
44ad9f4 Added more rdoc-friendly comments, and then buried the lot into a mod…
dagbrown authored
289 def log(message)
618301b @dagbrown Added a bunch of documentation to gurgitate-mail.RB
authored
290 if @logfile then
44ad9f4 Added more rdoc-friendly comments, and then buried the lot into a mod…
dagbrown authored
291 File.open(@logfile,"a") do |f|
292 f.flock(File::LOCK_EX)
293 f.print(Time.new.to_s+" "+message+"\n")
294 f.flock(File::LOCK_UN)
295 end
e55fb7a Adding the gurgitate-mail files to rubyforge so that others may find …
dagbrown authored
296 end
297 end
298
1d4dbf4 1) Made the comments more rdoc-friendly
dagbrown authored
299 # Pipes the message through +program+. If +program+
bdbfd6a @dagbrown Merging a couple of branches together, a file at a time.
authored
300 # fails, puts the message into +spoolfile+
44ad9f4 Added more rdoc-friendly comments, and then buried the lot into a mod…
dagbrown authored
301 def pipe(program)
302 self.log "Piping through "+program
303 IO.popen(program,"w") do |f|
304 f.print(self.to_s)
305 end
306 return $?>>8
e55fb7a Adding the gurgitate-mail files to rubyforge so that others may find …
dagbrown authored
307 end
308
1d4dbf4 1) Made the comments more rdoc-friendly
dagbrown authored
309 # Pipes the message through +program+, and returns another
310 # +Gurgitate+ object containing the output of the filter
618301b @dagbrown Added a bunch of documentation to gurgitate-mail.RB
authored
311 #
312 # Use it like this:
313 #
314 # filter "bogofilter -p" do
315 # if x_bogosity =~ /Spam/ then
316 # log "Found spam"
317 # delete
318 # return
319 # end
320 # end
321 #
dfec1a1 Added stuff for site-wide config files.
dagbrown authored
322 def filter(program,&block)
44ad9f4 Added more rdoc-friendly comments, and then buried the lot into a mod…
dagbrown authored
323 self.log "Filtering with "+program
324 IO.popen("-","w+") do |filter|
84903a7 Changed a couple of small things for Ruby 1.9 compatibility:
dagbrown authored
325 unless filter then
326 begin
327 exec(program)
328 rescue
329 exit! # should not get here anyway
330 end
e55fb7a Adding the gurgitate-mail files to rubyforge so that others may find …
dagbrown authored
331 else
44ad9f4 Added more rdoc-friendly comments, and then buried the lot into a mod…
dagbrown authored
332 if fork
333 filter.close_write
dfec1a1 Added stuff for site-wide config files.
dagbrown authored
334 g=Gurgitate.new(filter)
5a65662 - Added a thing to the "filter" method so that you can say something …
dagbrown authored
335 g.instance_eval(&block) if block_given?
336 return g
44ad9f4 Added more rdoc-friendly comments, and then buried the lot into a mod…
dagbrown authored
337 else
84903a7 Changed a couple of small things for Ruby 1.9 compatibility:
dagbrown authored
338 begin
339 filter.close_read
340 filter.print to_s
341 filter.close
342 rescue
343 nil
344 ensure
345 exit!
346 end
44ad9f4 Added more rdoc-friendly comments, and then buried the lot into a mod…
dagbrown authored
347 end
e55fb7a Adding the gurgitate-mail files to rubyforge so that others may find …
dagbrown authored
348 end
349 end
350 end
351
618301b @dagbrown Added a bunch of documentation to gurgitate-mail.RB
authored
352 def process(&block) #:nodoc:
84903a7 Changed a couple of small things for Ruby 1.9 compatibility:
dagbrown authored
353 begin
354 if @rules.size > 0 or block
355 @rules.each do |configfilespec|
356 begin
357 eval File.new(configfilespec).read, nil,
358 configfilespec
359 rescue ScriptError
360 log "Couldn't load #{configfilespec}: "+$!
361 save(spoolfile)
362 rescue Exception
363 log "Error while executing #{configfilespec}: #{$!}"
364 $@.each { |tr| log "Backtrace: #{tr}" }
5093eaf @dagbrown Making a bunch of warnings and an error go away.
authored
365 folderstyle MBox
84903a7 Changed a couple of small things for Ruby 1.9 compatibility:
dagbrown authored
366 save(spoolfile)
367 end
969ab41 Merged in Aria's changes to handle systemwide configuration.
dagbrown authored
368 end
84903a7 Changed a couple of small things for Ruby 1.9 compatibility:
dagbrown authored
369 if block
370 instance_eval(&block)
371 end
372 log "Mail not covered by rules, saving to default spool"
373 save(spoolfile)
374 else
375 save(spoolfile)
969ab41 Merged in Aria's changes to handle systemwide configuration.
dagbrown authored
376 end
84903a7 Changed a couple of small things for Ruby 1.9 compatibility:
dagbrown authored
377 rescue Exception
378 log "Error while executing rules: #{$!}"
379 $@.each { |tr| log "Backtrace: #{tr}" }
380 log "Attempting to save to spoolfile after error"
5093eaf @dagbrown Making a bunch of warnings and an error go away.
authored
381 folderstyle MBox
e6593fd Fixed up a misplaced "else". D'oh.
dagbrown authored
382 save(spoolfile)
44ad9f4 Added more rdoc-friendly comments, and then buried the lot into a mod…
dagbrown authored
383 end
969ab41 Merged in Aria's changes to handle systemwide configuration.
dagbrown authored
384 end
385 end
386 end
Something went wrong with that request. Please try again.