gurgitate-mail 1.10.3 → 1.10.5
Sign up to get free protection for your applications and to get access to all the features.
- data/.gemtest +0 -0
- data/Rakefile +15 -0
- data/bin/gurgitate-mail +1 -1
- data/test/gurgitate-test.rb +3 -1
- metadata +36 -16
- data/lib/gurgitate-mail.rb +0 -385
- data/lib/gurgitate/deliver.rb +0 -86
- data/lib/gurgitate/deliver/maildir.rb +0 -104
- data/lib/gurgitate/deliver/mbox.rb +0 -47
- data/lib/gurgitate/deliver/mh.rb +0 -147
- data/lib/gurgitate/header.rb +0 -81
- data/lib/gurgitate/headers.rb +0 -282
- data/lib/gurgitate/mail_headers.rb +0 -166
- data/lib/gurgitate/mailmessage.rb +0 -124
- data/lib/gurgitate/message.rb +0 -113
data/.gemtest
ADDED
File without changes
|
data/Rakefile
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
Tests = Dir["test/test_*.rb"]
|
2
|
+
|
3
|
+
|
4
|
+
task :test do
|
5
|
+
$:.unshift File.dirname(__FILE__)
|
6
|
+
require './test/runtests'
|
7
|
+
|
8
|
+
testcases = Tests.map do |file|
|
9
|
+
load file
|
10
|
+
eval("TC_" + File.basename(file,".rb").sub(/^test_/,'').capitalize)
|
11
|
+
end
|
12
|
+
|
13
|
+
runtests testcases
|
14
|
+
end
|
15
|
+
|
data/bin/gurgitate-mail
CHANGED
data/test/gurgitate-test.rb
CHANGED
@@ -12,12 +12,14 @@ require 'pathname'
|
|
12
12
|
require 'irb'
|
13
13
|
require "gurgitate-mail"
|
14
14
|
require "etc"
|
15
|
+
require "tmpdir"
|
15
16
|
|
16
17
|
class GurgitateTest < Test::Unit::TestCase
|
17
18
|
def setup
|
18
19
|
currentdir = Pathname.new(File.join(File.dirname(__FILE__),
|
19
20
|
"..")).realpath.to_s
|
20
|
-
@testdir =
|
21
|
+
@testdir = Dir.mktmpdir
|
22
|
+
# @testdir = File.join(currentdir,"test-data")
|
21
23
|
@folders = File.join(@testdir,"folders")
|
22
24
|
FileUtils.rmtree @testdir if File.exists? @testdir
|
23
25
|
Dir.mkdir @testdir
|
metadata
CHANGED
@@ -1,15 +1,21 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: gurgitate-mail
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
|
4
|
+
hash: 53
|
5
|
+
prerelease: false
|
6
|
+
segments:
|
7
|
+
- 1
|
8
|
+
- 10
|
9
|
+
- 5
|
10
|
+
version: 1.10.5
|
5
11
|
platform: ruby
|
6
12
|
authors:
|
7
13
|
- Dave Brown
|
8
|
-
autorequire:
|
14
|
+
autorequire:
|
9
15
|
bindir: bin
|
10
16
|
cert_chain: []
|
11
17
|
|
12
|
-
date:
|
18
|
+
date: 2010-12-27 00:00:00 +09:00
|
13
19
|
default_executable: gurgitate-mail
|
14
20
|
dependencies: []
|
15
21
|
|
@@ -22,16 +28,6 @@ extensions: []
|
|
22
28
|
extra_rdoc_files: []
|
23
29
|
|
24
30
|
files:
|
25
|
-
- lib/gurgitate-mail.rb
|
26
|
-
- lib/gurgitate/deliver/mh.rb
|
27
|
-
- lib/gurgitate/deliver/mbox.rb
|
28
|
-
- lib/gurgitate/deliver/maildir.rb
|
29
|
-
- lib/gurgitate/message.rb
|
30
|
-
- lib/gurgitate/header.rb
|
31
|
-
- lib/gurgitate/mail_headers.rb
|
32
|
-
- lib/gurgitate/deliver.rb
|
33
|
-
- lib/gurgitate/mailmessage.rb
|
34
|
-
- lib/gurgitate/headers.rb
|
35
31
|
- test/test_header.rb
|
36
32
|
- test/runtests.rb
|
37
33
|
- test/test_delivery.rb
|
@@ -48,6 +44,9 @@ files:
|
|
48
44
|
- test/test_execute_rules.rb
|
49
45
|
- test/test_configuration.rb
|
50
46
|
- test/test_headers.rb
|
47
|
+
- .gemtest
|
48
|
+
- Rakefile
|
49
|
+
- bin/gurgitate-mail
|
51
50
|
has_rdoc: true
|
52
51
|
homepage: http://www.rubyforge.org/projects/gurgitate-mail/
|
53
52
|
licenses: []
|
@@ -58,23 +57,44 @@ rdoc_options: []
|
|
58
57
|
require_paths:
|
59
58
|
- lib
|
60
59
|
required_ruby_version: !ruby/object:Gem::Requirement
|
60
|
+
none: false
|
61
61
|
requirements:
|
62
62
|
- - ">="
|
63
63
|
- !ruby/object:Gem::Version
|
64
|
+
hash: 3
|
65
|
+
segments:
|
66
|
+
- 0
|
64
67
|
version: "0"
|
65
|
-
version:
|
66
68
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
69
|
+
none: false
|
67
70
|
requirements:
|
68
71
|
- - ">="
|
69
72
|
- !ruby/object:Gem::Version
|
73
|
+
hash: 3
|
74
|
+
segments:
|
75
|
+
- 0
|
70
76
|
version: "0"
|
71
|
-
version:
|
72
77
|
requirements: []
|
73
78
|
|
74
79
|
rubyforge_project: gurgitate-mail
|
75
|
-
rubygems_version: 1.3.
|
80
|
+
rubygems_version: 1.3.7
|
76
81
|
signing_key:
|
77
82
|
specification_version: 3
|
78
83
|
summary: gurgitate-mail is a mail filter (and a mail-delivery agent)
|
79
84
|
test_files:
|
85
|
+
- test/test_header.rb
|
80
86
|
- test/runtests.rb
|
87
|
+
- test/test_delivery.rb
|
88
|
+
- test/test_headers_meddling_with_headers.rb
|
89
|
+
- test/test_rules.rb
|
90
|
+
- test/test_writing.rb
|
91
|
+
- test/test_headers_creating_from_hash.rb
|
92
|
+
- test/test_mail_headers.rb
|
93
|
+
- test/test_deliver.rb
|
94
|
+
- test/test_gurgitate_delivery.rb
|
95
|
+
- test/gurgitate-test.rb
|
96
|
+
- test/test_process.rb
|
97
|
+
- test/test_mail_headers_meddling_with_headers.rb
|
98
|
+
- test/test_execute_rules.rb
|
99
|
+
- test/test_configuration.rb
|
100
|
+
- test/test_headers.rb
|
data/lib/gurgitate-mail.rb
DELETED
@@ -1,385 +0,0 @@
|
|
1
|
-
#!/opt/bin/ruby
|
2
|
-
#------------------------------------------------------------------------
|
3
|
-
# Mail filter package
|
4
|
-
#------------------------------------------------------------------------
|
5
|
-
|
6
|
-
require 'etc'
|
7
|
-
|
8
|
-
require 'gurgitate/mailmessage'
|
9
|
-
require 'gurgitate/deliver'
|
10
|
-
|
11
|
-
module Gurgitate
|
12
|
-
# This is the actual gurgitator; it reads a message and then it can
|
13
|
-
# do other stuff with it, like saving it to a mailbox or forwarding
|
14
|
-
# it somewhere else.
|
15
|
-
#
|
16
|
-
# To set configuration parameters for gurgitate-mail, use a keyword-
|
17
|
-
# based system. It's almost like an attribute, only if you give the
|
18
|
-
# accessor a parameter, it will set the configuration parameter to
|
19
|
-
# the parameter's value. For instance:
|
20
|
-
#
|
21
|
-
# maildir "#{homedir}/Mail"
|
22
|
-
# sendmail "/usr/sbin/sendmail"
|
23
|
-
# spoolfile "Maildir"
|
24
|
-
# spooldir homedir
|
25
|
-
#
|
26
|
-
# (This is because of an oddity in Ruby where, even if an
|
27
|
-
# accessor exists in the current object, if you say:
|
28
|
-
# name = value
|
29
|
-
# it'll always create a local variable. Not quite what you
|
30
|
-
# want when you're trying to set a config parameter. You have
|
31
|
-
# to say <code>self.name = value</code>, which [I think] is ugly.
|
32
|
-
#
|
33
|
-
# In the interests of promoting harmony, of course,
|
34
|
-
# <code>self.name = value</code> still works.)
|
35
|
-
#
|
36
|
-
# The attributes you can define are:
|
37
|
-
#
|
38
|
-
# homedir :: Your home directory. This defaults to what your
|
39
|
-
# actual home directory is.
|
40
|
-
#
|
41
|
-
# maildir :: The directory you store your mail in. This defaults
|
42
|
-
# to the "Mail" directory in your home dir.
|
43
|
-
#
|
44
|
-
# logfile :: The path to your gurgitate-mail log file. If you
|
45
|
-
# set this to +nil+, gurgitate-mail won't log anything.
|
46
|
-
# The default value is ".gurgitate.log" in your home
|
47
|
-
# directory.
|
48
|
-
#
|
49
|
-
# The following parameters are more likely to be interesting to the
|
50
|
-
# system administrator than the everyday user.
|
51
|
-
#
|
52
|
-
# sendmail :: The full path of your "sendmail" program, or at least
|
53
|
-
# a program that provides functionality equivalent to
|
54
|
-
# sendmail.
|
55
|
-
#
|
56
|
-
# spoolfile :: The default location to store mail messages, for the
|
57
|
-
# messages that have been unaffected by your gurgitate
|
58
|
-
# rules. If an exception is raised by your rules, the
|
59
|
-
# message will be delivered to the spoolfile.
|
60
|
-
#
|
61
|
-
# spooldir :: The location where users' system mail boxes live.
|
62
|
-
#
|
63
|
-
# folderstyle :: The style of mailbox to create (and to expect,
|
64
|
-
# although gurgitate-mail automatically detects the
|
65
|
-
# type of existing mailboxes). See the separate
|
66
|
-
# documentation for folderstyle for more details.
|
67
|
-
class Gurgitate < Mailmessage
|
68
|
-
include Deliver
|
69
|
-
|
70
|
-
# Instead of the usual attributes, I went with a
|
71
|
-
# reader-is-writer type thing (as seen quite often in Perl and
|
72
|
-
# C++ code) so that in your .gurgitate-rules, you can say
|
73
|
-
#
|
74
|
-
# maildir "#{homedir}/Mail"
|
75
|
-
# sendmail "/usr/sbin/sendmail"
|
76
|
-
# spoolfile "Maildir"
|
77
|
-
# spooldir homedir
|
78
|
-
#
|
79
|
-
# This is because of an oddity in Ruby where, even if an
|
80
|
-
# accessor exists in the current object, if you say:
|
81
|
-
# name = value
|
82
|
-
# it'll always create a local variable. Not quite what you
|
83
|
-
# want when you're trying to set a config parameter. You have
|
84
|
-
# to say "self.name = value", which (I think) is ugly.
|
85
|
-
#
|
86
|
-
# In the interests of promoting harmony, of course, the previous
|
87
|
-
# syntax will continue to work.
|
88
|
-
def self.attr_configparam(*syms)
|
89
|
-
syms.each do |sym|
|
90
|
-
class_eval %{
|
91
|
-
def #{sym} (*vals)
|
92
|
-
if vals.length == 1
|
93
|
-
@#{sym} = vals[0]
|
94
|
-
elsif vals.length == 0
|
95
|
-
@#{sym}
|
96
|
-
else
|
97
|
-
raise ArgumentError,
|
98
|
-
"wrong number of arguments " +
|
99
|
-
"(\#{vals.length} for 0 or 1)"
|
100
|
-
end
|
101
|
-
end
|
102
|
-
|
103
|
-
# Don't break it for the nice people who use
|
104
|
-
# old-style accessors though. Breaking people's
|
105
|
-
# .gurgitate-rules is a bad idea.
|
106
|
-
attr_writer :#{sym}
|
107
|
-
}
|
108
|
-
end
|
109
|
-
end
|
110
|
-
|
111
|
-
# The directory you want to put mail folders into
|
112
|
-
attr_configparam :maildir
|
113
|
-
|
114
|
-
# The path to your log file
|
115
|
-
attr_configparam :logfile
|
116
|
-
|
117
|
-
# The full path of your "sendmail" program
|
118
|
-
attr_configparam :sendmail
|
119
|
-
|
120
|
-
# Your home directory
|
121
|
-
attr_configparam :homedir
|
122
|
-
|
123
|
-
# Your default mail spool
|
124
|
-
attr_configparam :spoolfile
|
125
|
-
|
126
|
-
# The directory where user mail spools live
|
127
|
-
attr_configparam :spooldir
|
128
|
-
|
129
|
-
# What kind of mailboxes you prefer
|
130
|
-
# attr_configparam :folderstyle
|
131
|
-
|
132
|
-
# What kind of mailboxes you prefer. Treat this like a
|
133
|
-
# configuration parameter. If no argument is given, then
|
134
|
-
# return the current default type.
|
135
|
-
#
|
136
|
-
# Depending on what you set this to, some other configuration
|
137
|
-
# parameters change. You can set this to the following things:
|
138
|
-
#
|
139
|
-
# <code>Maildir</code> :: Create Maildir mailboxes.
|
140
|
-
#
|
141
|
-
# This sets +spooldir+ to your home
|
142
|
-
# directory, +spoolfile+ to
|
143
|
-
# $HOME/Maildir and creates
|
144
|
-
# mail folders underneath that.
|
145
|
-
#
|
146
|
-
# <code>MH</code> :: Create MH mail boxes.
|
147
|
-
#
|
148
|
-
# This reads your <code>.mh_profile</code>
|
149
|
-
# file to find out where you've told MH to
|
150
|
-
# find its mail folders, and uses that value.
|
151
|
-
# If it can't find that in your .mh_profile,
|
152
|
-
# it will assume you want mailboxes in
|
153
|
-
# $HOME/Mail. It sets +spoolfile+ to
|
154
|
-
# "inbox" in your mail directory.
|
155
|
-
#
|
156
|
-
# <code>Mbox</code> :: Create +mbox+ mailboxes.
|
157
|
-
#
|
158
|
-
# This sets +spooldir+ to
|
159
|
-
# <code>/var/spool/mail</code> and
|
160
|
-
# +spoolfile+ to a file with your username
|
161
|
-
# in <code>/var/spool/mail</code>.
|
162
|
-
def folderstyle(*style)
|
163
|
-
if style.length == 0 then
|
164
|
-
@folderstyle
|
165
|
-
elsif style.length == 1 then
|
166
|
-
if style[0] == Maildir then
|
167
|
-
spooldir homedir
|
168
|
-
spoolfile File.join(spooldir,"Maildir")
|
169
|
-
maildir spoolfile
|
170
|
-
elsif style[0] == MH then
|
171
|
-
mh_profile_path = File.join(ENV["HOME"],".mh_profile")
|
172
|
-
if File.exists?(mh_profile_path) then
|
173
|
-
mh_profile = YAML.load(File.read(mh_profile_path))
|
174
|
-
maildir mh_profile["Path"]
|
175
|
-
else
|
176
|
-
maildir File.join(ENV["HOME"],"Mail")
|
177
|
-
end
|
178
|
-
spoolfile File.join(maildir,"inbox")
|
179
|
-
else
|
180
|
-
spooldir "/var/spool/mail"
|
181
|
-
spoolfile File.join(spooldir, @passwd.name)
|
182
|
-
end
|
183
|
-
|
184
|
-
@folderstyle = style[0]
|
185
|
-
else
|
186
|
-
raise ArgumentError, "wrong number of arguments "+
|
187
|
-
"(#{style.length} for 0 or 1)"
|
188
|
-
end
|
189
|
-
@folderstyle
|
190
|
-
end
|
191
|
-
|
192
|
-
# Set config params to defaults, read in mail message from
|
193
|
-
# +input+
|
194
|
-
# input::
|
195
|
-
# Either the text of the email message in RFC-822 format,
|
196
|
-
# or a filehandle where the email message can be read from
|
197
|
-
# recipient::
|
198
|
-
# The contents of the envelope recipient parameter
|
199
|
-
# sender::
|
200
|
-
# The envelope sender parameter
|
201
|
-
# spooldir::
|
202
|
-
# The location of the mail spools directory.
|
203
|
-
def initialize(input=nil,
|
204
|
-
recipient=nil,
|
205
|
-
sender=nil,
|
206
|
-
spooldir="/var/spool/mail",
|
207
|
-
&block)
|
208
|
-
@passwd = Etc.getpwuid
|
209
|
-
@homedir = @passwd.dir;
|
210
|
-
@maildir = File.join(@passwd.dir,"Mail")
|
211
|
-
@logfile = File.join(@passwd.dir,".gurgitate.log")
|
212
|
-
@sendmail = "/usr/lib/sendmail"
|
213
|
-
@spooldir = spooldir
|
214
|
-
@spoolfile = File.join(@spooldir,@passwd.name )
|
215
|
-
@folderstyle = MBox
|
216
|
-
@rules = []
|
217
|
-
|
218
|
-
input_text = ""
|
219
|
-
input.each_line do |l| input_text << l end
|
220
|
-
super(input_text, recipient, sender)
|
221
|
-
instance_eval(&block) if block_given?
|
222
|
-
end
|
223
|
-
|
224
|
-
def add_rules(filename, options = {}) #:nodoc:
|
225
|
-
if not Hash === options
|
226
|
-
raise ArgumentError.new("Expected hash of options")
|
227
|
-
end
|
228
|
-
if filename == :default
|
229
|
-
filename=homedir+"/.gurgitate-rules"
|
230
|
-
end
|
231
|
-
if not FileTest.exist?(filename)
|
232
|
-
filename = filename + '.rb'
|
233
|
-
end
|
234
|
-
if not FileTest.exist?(filename)
|
235
|
-
if options.has_key?(:user)
|
236
|
-
log("#{filename} does not exist.")
|
237
|
-
end
|
238
|
-
return false
|
239
|
-
end
|
240
|
-
if FileTest.file?(filename) and
|
241
|
-
( ( not options.has_key? :system and
|
242
|
-
FileTest.owned?(filename) ) or
|
243
|
-
( options.has_key? :system and
|
244
|
-
options[:system] == true and
|
245
|
-
File.stat(filename).uid == 0 ) ) and
|
246
|
-
FileTest.readable?(filename)
|
247
|
-
@rules << filename
|
248
|
-
else
|
249
|
-
log("#{filename} has bad permissions or ownership, not using rules")
|
250
|
-
return false
|
251
|
-
end
|
252
|
-
end
|
253
|
-
|
254
|
-
# Deletes (discards) the current message.
|
255
|
-
def delete
|
256
|
-
# Well, nothing here, really.
|
257
|
-
end
|
258
|
-
|
259
|
-
# This is kind of neat. You can get a header by calling its
|
260
|
-
# name as a method. For example, if you want the header
|
261
|
-
# "X-Face", then you call x_face and that gets it for you. It
|
262
|
-
# raises NameError if that header isn't found.
|
263
|
-
#
|
264
|
-
# meth::
|
265
|
-
# The method that the caller tried to call which isn't
|
266
|
-
# handled any other way.
|
267
|
-
def method_missing(meth)
|
268
|
-
headername=meth.to_s.split(/_/).map {|x| x.capitalize}.join("-")
|
269
|
-
if headers[headername] then
|
270
|
-
return headers[headername]
|
271
|
-
else
|
272
|
-
raise NameError,"undefined local variable or method, or header not found `#{meth}' for #{self}:#{self.class}"
|
273
|
-
end
|
274
|
-
end
|
275
|
-
|
276
|
-
# Forwards the message to +address+.
|
277
|
-
#
|
278
|
-
# address::
|
279
|
-
# A valid email address to forward the message to.
|
280
|
-
def forward(address)
|
281
|
-
self.log "Forwarding to "+address
|
282
|
-
IO.popen(@sendmail+" "+address,"w") do |f|
|
283
|
-
f.print(self.to_s)
|
284
|
-
end
|
285
|
-
end
|
286
|
-
|
287
|
-
# Writes +message+ to the log file.
|
288
|
-
def log(message)
|
289
|
-
if @logfile then
|
290
|
-
File.open(@logfile,"a") do |f|
|
291
|
-
f.flock(File::LOCK_EX)
|
292
|
-
f.print(Time.new.to_s+" "+message+"\n")
|
293
|
-
f.flock(File::LOCK_UN)
|
294
|
-
end
|
295
|
-
end
|
296
|
-
end
|
297
|
-
|
298
|
-
# Pipes the message through +program+. If +program+
|
299
|
-
# fails, puts the message into +spoolfile+
|
300
|
-
def pipe(program)
|
301
|
-
self.log "Piping through "+program
|
302
|
-
IO.popen(program,"w") do |f|
|
303
|
-
f.print(self.to_s)
|
304
|
-
end
|
305
|
-
return $?>>8
|
306
|
-
end
|
307
|
-
|
308
|
-
# Pipes the message through +program+, and returns another
|
309
|
-
# +Gurgitate+ object containing the output of the filter
|
310
|
-
#
|
311
|
-
# Use it like this:
|
312
|
-
#
|
313
|
-
# filter "bogofilter -p" do
|
314
|
-
# if x_bogosity =~ /Spam/ then
|
315
|
-
# log "Found spam"
|
316
|
-
# delete
|
317
|
-
# return
|
318
|
-
# end
|
319
|
-
# end
|
320
|
-
#
|
321
|
-
def filter(program,&block)
|
322
|
-
self.log "Filtering with "+program
|
323
|
-
IO.popen("-","w+") do |filter|
|
324
|
-
unless filter then
|
325
|
-
begin
|
326
|
-
exec(program)
|
327
|
-
rescue
|
328
|
-
exit! # should not get here anyway
|
329
|
-
end
|
330
|
-
else
|
331
|
-
if fork
|
332
|
-
filter.close_write
|
333
|
-
g=Gurgitate.new(filter)
|
334
|
-
g.instance_eval(&block) if block_given?
|
335
|
-
return g
|
336
|
-
else
|
337
|
-
begin
|
338
|
-
filter.close_read
|
339
|
-
filter.print to_s
|
340
|
-
filter.close
|
341
|
-
rescue
|
342
|
-
nil
|
343
|
-
ensure
|
344
|
-
exit!
|
345
|
-
end
|
346
|
-
end
|
347
|
-
end
|
348
|
-
end
|
349
|
-
end
|
350
|
-
|
351
|
-
def process(&block) #:nodoc:
|
352
|
-
begin
|
353
|
-
if @rules.size > 0 or block
|
354
|
-
@rules.each do |configfilespec|
|
355
|
-
begin
|
356
|
-
eval File.new(configfilespec).read, nil,
|
357
|
-
configfilespec
|
358
|
-
rescue ScriptError
|
359
|
-
log "Couldn't load #{configfilespec}: "+$!
|
360
|
-
save(spoolfile)
|
361
|
-
rescue Exception
|
362
|
-
log "Error while executing #{configfilespec}: #{$!}"
|
363
|
-
$@.each { |tr| log "Backtrace: #{tr}" }
|
364
|
-
folderstyle = MBox
|
365
|
-
save(spoolfile)
|
366
|
-
end
|
367
|
-
end
|
368
|
-
if block
|
369
|
-
instance_eval(&block)
|
370
|
-
end
|
371
|
-
log "Mail not covered by rules, saving to default spool"
|
372
|
-
save(spoolfile)
|
373
|
-
else
|
374
|
-
save(spoolfile)
|
375
|
-
end
|
376
|
-
rescue Exception
|
377
|
-
log "Error while executing rules: #{$!}"
|
378
|
-
$@.each { |tr| log "Backtrace: #{tr}" }
|
379
|
-
log "Attempting to save to spoolfile after error"
|
380
|
-
folderstyle = MBox
|
381
|
-
save(spoolfile)
|
382
|
-
end
|
383
|
-
end
|
384
|
-
end
|
385
|
-
end
|