gurgitate-mail 1.10.0 → 1.10.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/bin/gurgitate-mail +0 -0
- data/lib/gurgitate-mail.rb +107 -15
- data/lib/gurgitate/deliver/maildir.rb +4 -4
- data/lib/gurgitate/deliver/mbox.rb +5 -5
- data/lib/gurgitate/deliver/mh.rb +15 -15
- data/lib/gurgitate/headers.rb +10 -7
- data/lib/gurgitate/mail_headers.rb +166 -0
- data/lib/gurgitate/mailmessage.rb +56 -27
- data/lib/gurgitate/message.rb +113 -0
- data/test/gurgitate-test.rb +7 -1
- data/test/runtests.rb +16 -7
- data/test/test_configuration.rb +6 -0
- data/test/test_deliver.rb +6 -0
- data/test/test_delivery.rb +6 -0
- data/test/test_execute_rules.rb +43 -0
- data/test/test_gurgitate_delivery.rb +52 -57
- data/test/test_header.rb +19 -7
- data/test/test_headers.rb +60 -84
- data/test/test_headers_creating_from_hash.rb +18 -0
- data/test/test_headers_meddling_with_headers.rb +57 -0
- data/test/test_mail_headers.rb +429 -0
- data/test/test_mail_headers_meddling_with_headers.rb +60 -0
- data/test/test_process.rb +2 -1
- data/test/test_rules.rb +9 -1
- data/test/test_writing.rb +1 -2
- metadata +60 -44
data/bin/gurgitate-mail
CHANGED
File without changes
|
data/lib/gurgitate-mail.rb
CHANGED
@@ -9,10 +9,61 @@ require 'gurgitate/mailmessage'
|
|
9
9
|
require 'gurgitate/deliver'
|
10
10
|
|
11
11
|
module Gurgitate
|
12
|
-
|
13
|
-
#
|
14
|
-
# other stuff with it, like save to a mailbox or forward
|
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
|
15
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.
|
16
67
|
class Gurgitate < Mailmessage
|
17
68
|
include Deliver
|
18
69
|
|
@@ -36,7 +87,7 @@ module Gurgitate
|
|
36
87
|
# syntax will continue to work.
|
37
88
|
def self.attr_configparam(*syms)
|
38
89
|
syms.each do |sym|
|
39
|
-
class_eval
|
90
|
+
class_eval %{
|
40
91
|
def #{sym} (*vals)
|
41
92
|
if vals.length == 1
|
42
93
|
@#{sym} = vals[0]
|
@@ -53,7 +104,7 @@ module Gurgitate
|
|
53
104
|
# old-style accessors though. Breaking people's
|
54
105
|
# .gurgitate-rules is a bad idea.
|
55
106
|
attr_writer :#{sym}
|
56
|
-
|
107
|
+
}
|
57
108
|
end
|
58
109
|
end
|
59
110
|
|
@@ -78,7 +129,36 @@ module Gurgitate
|
|
78
129
|
# What kind of mailboxes you prefer
|
79
130
|
# attr_configparam :folderstyle
|
80
131
|
|
81
|
-
# What kind of mailboxes you prefer
|
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>.
|
82
162
|
def folderstyle(*style)
|
83
163
|
if style.length == 0 then
|
84
164
|
@folderstyle
|
@@ -141,7 +221,7 @@ module Gurgitate
|
|
141
221
|
instance_eval(&block) if block_given?
|
142
222
|
end
|
143
223
|
|
144
|
-
def add_rules(filename, options = {})
|
224
|
+
def add_rules(filename, options = {}) #:nodoc:
|
145
225
|
if not Hash === options
|
146
226
|
raise ArgumentError.new("Expected hash of options")
|
147
227
|
end
|
@@ -171,15 +251,16 @@ module Gurgitate
|
|
171
251
|
end
|
172
252
|
end
|
173
253
|
|
174
|
-
# Deletes the current message.
|
254
|
+
# Deletes (discards) the current message.
|
175
255
|
def delete
|
176
256
|
# Well, nothing here, really.
|
177
257
|
end
|
178
258
|
|
179
|
-
# This is
|
180
|
-
#
|
181
|
-
# x_face and that gets it for you. It
|
182
|
-
# that header isn't found.
|
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
|
+
#
|
183
264
|
# meth::
|
184
265
|
# The method that the caller tried to call which isn't
|
185
266
|
# handled any other way.
|
@@ -193,6 +274,7 @@ module Gurgitate
|
|
193
274
|
end
|
194
275
|
|
195
276
|
# Forwards the message to +address+.
|
277
|
+
#
|
196
278
|
# address::
|
197
279
|
# A valid email address to forward the message to.
|
198
280
|
def forward(address)
|
@@ -204,7 +286,7 @@ module Gurgitate
|
|
204
286
|
|
205
287
|
# Writes +message+ to the log file.
|
206
288
|
def log(message)
|
207
|
-
if
|
289
|
+
if @logfile then
|
208
290
|
File.open(@logfile,"a") do |f|
|
209
291
|
f.flock(File::LOCK_EX)
|
210
292
|
f.print(Time.new.to_s+" "+message+"\n")
|
@@ -225,6 +307,17 @@ module Gurgitate
|
|
225
307
|
|
226
308
|
# Pipes the message through +program+, and returns another
|
227
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
|
+
#
|
228
321
|
def filter(program,&block)
|
229
322
|
self.log "Filtering with "+program
|
230
323
|
IO.popen("-","w+") do |filter|
|
@@ -255,8 +348,7 @@ module Gurgitate
|
|
255
348
|
end
|
256
349
|
end
|
257
350
|
|
258
|
-
|
259
|
-
def process(&block)
|
351
|
+
def process(&block) #:nodoc:
|
260
352
|
begin
|
261
353
|
if @rules.size > 0 or block
|
262
354
|
@rules.each do |configfilespec|
|
@@ -15,7 +15,7 @@ module Gurgitate
|
|
15
15
|
# message to. If it is of the form "=mailbox", it saves
|
16
16
|
# the message to +Maildir+/+mailbox+. Otherwise, it
|
17
17
|
# simply saves the message to the file +mailbox+.
|
18
|
-
def self::check_mailbox
|
18
|
+
def self::check_mailbox mailbox
|
19
19
|
begin
|
20
20
|
if File.stat(mailbox).directory? then
|
21
21
|
if File.stat(File.join(mailbox,"cur")).directory? then
|
@@ -33,7 +33,7 @@ module Gurgitate
|
|
33
33
|
# One of "+mailbox+/tmp" or "+mailbox+/new", but that's
|
34
34
|
# only because that's what the maildir spec
|
35
35
|
# (http://cr.yp.to/proto/maildir.html) says.
|
36
|
-
def maildir_getfilename
|
36
|
+
def maildir_getfilename dir
|
37
37
|
time=Time.now.to_f
|
38
38
|
counter=0
|
39
39
|
hostname=Socket::gethostname
|
@@ -50,7 +50,7 @@ module Gurgitate
|
|
50
50
|
# Creates a new Maildir folder +mailbox+
|
51
51
|
# mailbox::
|
52
52
|
# The full path of the new folder to be created
|
53
|
-
def make_mailbox
|
53
|
+
def make_mailbox mailbox
|
54
54
|
Dir.mkdir(mailbox)
|
55
55
|
%w{cur tmp new}.each do |dir|
|
56
56
|
Dir.mkdir(File.join(mailbox,dir))
|
@@ -63,7 +63,7 @@ module Gurgitate
|
|
63
63
|
# message to. If it is of the form "=mailbox", it saves
|
64
64
|
# the message to +Maildir+/+mailbox+. Otherwise, it
|
65
65
|
# simply saves the message to the file +mailbox+.
|
66
|
-
def deliver_message
|
66
|
+
def deliver_message mailbox
|
67
67
|
begin
|
68
68
|
File.stat(mailbox)
|
69
69
|
rescue Errno::ENOENT
|
@@ -13,10 +13,10 @@ module Gurgitate
|
|
13
13
|
# the message to. If it is of the form "=mailbox", it
|
14
14
|
# saves the message to +Maildir+/+mailbox+. Otherwise,
|
15
15
|
# it simply saves the message to the file +mailbox+.
|
16
|
-
def self::check_mailbox
|
16
|
+
def self::check_mailbox mailbox
|
17
17
|
|
18
18
|
begin
|
19
|
-
if File.stat(mailbox).file? then
|
19
|
+
if File.stat(mailbox).file? then
|
20
20
|
return MBox
|
21
21
|
else
|
22
22
|
return nil
|
@@ -32,9 +32,9 @@ module Gurgitate
|
|
32
32
|
# the message to. If it is of the form "=mailbox", it
|
33
33
|
# saves the message to +Maildir+/+mailbox+. Otherwise,
|
34
34
|
# it simply saves the message to the file +mailbox+.
|
35
|
-
def deliver_message
|
36
|
-
File.open(mailbox,File::WRONLY |
|
37
|
-
File::APPEND |
|
35
|
+
def deliver_message mailbox
|
36
|
+
File.open(mailbox,File::WRONLY |
|
37
|
+
File::APPEND |
|
38
38
|
File::CREAT) do |f|
|
39
39
|
f.flock(File::LOCK_EX)
|
40
40
|
message=(if f.stat.size > 0 then "\n" else "" end) + to_mbox
|
data/lib/gurgitate/deliver/mh.rb
CHANGED
@@ -15,7 +15,7 @@ module Gurgitate
|
|
15
15
|
# the message to. If it is of the form "=mailbox", it
|
16
16
|
# saves the message to +Maildir+/+mailbox+. Otherwise,
|
17
17
|
# it simply saves the message to the file +mailbox+.
|
18
|
-
def self::check_mailbox
|
18
|
+
def self::check_mailbox mailbox
|
19
19
|
begin
|
20
20
|
# Rather annoyingly, pretty well any directory can
|
21
21
|
# be a MH mailbox, but this just checks to make sure
|
@@ -24,10 +24,10 @@ module Gurgitate
|
|
24
24
|
# I could put in a check for the path given in
|
25
25
|
# $HOME/.mh_profile, but Claws-Mail uses MH mailboxes and
|
26
26
|
# disregards $HOME/.mh_profile.
|
27
|
-
if File.stat(mailbox).directory? and
|
28
|
-
not ( File.exists?(File.join(mailbox, "cur")) or
|
29
|
-
File.exists?(File.join(mailbox, "tmp")) or
|
30
|
-
File.exists?(File.join(mailbox, "new"))) then
|
27
|
+
if File.stat(mailbox).directory? and
|
28
|
+
not ( File.exists?(File.join(mailbox, "cur")) or
|
29
|
+
File.exists?(File.join(mailbox, "tmp")) or
|
30
|
+
File.exists?(File.join(mailbox, "new"))) then
|
31
31
|
return MH
|
32
32
|
end
|
33
33
|
rescue Errno::ENOENT
|
@@ -41,7 +41,7 @@ module Gurgitate
|
|
41
41
|
# the message to. If it is of the form "=mailbox", it
|
42
42
|
# saves the message to +Maildir+/+mailbox+. Otherwise,
|
43
43
|
# it simply saves the message to the file +mailbox+.
|
44
|
-
def deliver_message
|
44
|
+
def deliver_message mailbox
|
45
45
|
if ! File.exists? mailbox then
|
46
46
|
Dir.mkdir(mailbox)
|
47
47
|
end
|
@@ -59,16 +59,16 @@ module Gurgitate
|
|
59
59
|
|
60
60
|
private
|
61
61
|
|
62
|
-
def update_sequences
|
62
|
+
def update_sequences mailbox, msgnum
|
63
63
|
sequences = File.join(mailbox, ".mh_sequences")
|
64
64
|
lockfile = sequences + ".lock" # how quaint
|
65
65
|
loop do
|
66
66
|
begin
|
67
|
-
File.open(lockfile,
|
68
|
-
File::WRONLY |
|
69
|
-
File::CREAT |
|
67
|
+
File.open(lockfile,
|
68
|
+
File::WRONLY |
|
69
|
+
File::CREAT |
|
70
70
|
File::EXCL ) do |lock|
|
71
|
-
File.open(sequences,
|
71
|
+
File.open(sequences,
|
72
72
|
File::RDWR | File::CREAT) do |seq|
|
73
73
|
|
74
74
|
seq.flock(File::LOCK_EX)
|
@@ -91,7 +91,7 @@ module Gurgitate
|
|
91
91
|
rescue Errno::EEXIST
|
92
92
|
# some other process is doing something, so wait a few
|
93
93
|
# milliseconds until it's done
|
94
|
-
sleep(0.01)
|
94
|
+
sleep(0.01)
|
95
95
|
end
|
96
96
|
end
|
97
97
|
end
|
@@ -115,13 +115,13 @@ module Gurgitate
|
|
115
115
|
end
|
116
116
|
end
|
117
117
|
|
118
|
-
def next_message
|
118
|
+
def next_message mailbox
|
119
119
|
next_msgnum = Dir.open(mailbox).map { |ent| ent.to_i }.max + 1
|
120
120
|
loop do
|
121
121
|
begin
|
122
122
|
File.open(File.join(mailbox, next_msgnum.to_s),
|
123
|
-
File::WRONLY |
|
124
|
-
File::CREAT |
|
123
|
+
File::WRONLY |
|
124
|
+
File::CREAT |
|
125
125
|
File::EXCL ) do |filehandle|
|
126
126
|
yield filehandle
|
127
127
|
end
|
data/lib/gurgitate/headers.rb
CHANGED
@@ -22,7 +22,7 @@ module Gurgitate
|
|
22
22
|
|
23
23
|
def sub(regex, replacement)
|
24
24
|
::Gurgitate::HeaderBag.new(
|
25
|
-
|
25
|
+
clone.map do |header|
|
26
26
|
::Gurgitate::Header.new(
|
27
27
|
"#{header.name}: " + header.contents.sub(regex,
|
28
28
|
replacement)
|
@@ -47,7 +47,7 @@ module Gurgitate
|
|
47
47
|
# Figures out whether the first line of a mail message is an
|
48
48
|
# mbox-style "From " line (say, if you get this from sendmail),
|
49
49
|
# or whether it's just a normal header.
|
50
|
-
# --
|
50
|
+
# --
|
51
51
|
# If you run "fetchmail" with the -m option to feed the
|
52
52
|
# mail message straight to gurgitate, skipping the "local
|
53
53
|
# MTA" step, then it doesn't have a "From " line. So I
|
@@ -121,7 +121,8 @@ module Gurgitate
|
|
121
121
|
@from=$+
|
122
122
|
end
|
123
123
|
else
|
124
|
-
fromregex=/([^ ]+@[^ ]+) \(.*\)|[^<]*[<](.*@.*)[>]|([^ ]+@[^ ]+
|
124
|
+
fromregex=/([^ ]+@[^ ]+) \(.*\)|[^<]*[<](.*@.*)[>]|([^ ]+@[^ ]+
|
125
|
+
)/
|
125
126
|
if self["Return-Path"] != nil then
|
126
127
|
fromregex.match(self["Return-Path"][0].contents)
|
127
128
|
else
|
@@ -135,12 +136,14 @@ module Gurgitate
|
|
135
136
|
# assume that it's local mail, and doesn't have an @ in its
|
136
137
|
# address.
|
137
138
|
unless address_candidate
|
138
|
-
if self["Return-Path"]
|
139
|
+
if self["Return-Path"] then
|
139
140
|
self["Return-Path"][0].contents =~ /(\S+)/
|
140
141
|
address_candidate=$+
|
141
142
|
else
|
142
|
-
self["From"]
|
143
|
-
|
143
|
+
if self["From"] then
|
144
|
+
self["From"][0].contents =~ /(\S+)/
|
145
|
+
address_candidate=$+
|
146
|
+
end
|
144
147
|
end
|
145
148
|
end
|
146
149
|
|
@@ -222,7 +225,7 @@ module Gurgitate
|
|
222
225
|
# Change the envelope from line to whatever you want. This might
|
223
226
|
# not be particularly neighborly, but oh well.
|
224
227
|
# newfrom:: An email address
|
225
|
-
def from=(newfrom)
|
228
|
+
def from=(newfrom)
|
226
229
|
@from=newfrom
|
227
230
|
@unix_from="From "+self.from+" "+Time.new.to_s
|
228
231
|
end
|
@@ -0,0 +1,166 @@
|
|
1
|
+
#!/opt/bin/ruby -w
|
2
|
+
|
3
|
+
require "gurgitate/headers"
|
4
|
+
|
5
|
+
module Gurgitate
|
6
|
+
class IllegalHeader < RuntimeError ; end
|
7
|
+
|
8
|
+
# ========================================================================
|
9
|
+
|
10
|
+
# A slightly bigger class for all of a message's headers
|
11
|
+
class MailHeaders < Headers
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
# Figures out whether the first line of a mail message is an
|
16
|
+
# mbox-style "From " line (say, if you get this from sendmail),
|
17
|
+
# or whether it's just a normal header.
|
18
|
+
# --
|
19
|
+
# If you run "fetchmail" with the -m option to feed the
|
20
|
+
# mail message straight to gurgitate, skipping the "local
|
21
|
+
# MTA" step, then it doesn't have a "From " line. So I
|
22
|
+
# have to deal with that by hand. First, check to see if
|
23
|
+
# there's a "From " line present in the first place.
|
24
|
+
def figure_out_from_line(headertext)
|
25
|
+
(unix_from,normal_headers) = headertext.split(/\n/,2)
|
26
|
+
|
27
|
+
if unix_from =~ /^From / then
|
28
|
+
headertext=normal_headers
|
29
|
+
unix_from=unix_from
|
30
|
+
else
|
31
|
+
# If there isn't, then deal with it after we've
|
32
|
+
# worried about the rest of the headers, 'cos we'll
|
33
|
+
# have to make our own.
|
34
|
+
unix_from=nil
|
35
|
+
end
|
36
|
+
return unix_from, headertext
|
37
|
+
end
|
38
|
+
|
39
|
+
# Get the envelope From information. This comes with a
|
40
|
+
# whole category of rants: this information is absurdly hard
|
41
|
+
# to get your hands on. The best you can manage is a sort
|
42
|
+
# of educated guess. Thus, this horrible glob of hackiness.
|
43
|
+
# I don't recommend looking too closely at this code if you
|
44
|
+
# can avoid it, and further I recommend making sure to
|
45
|
+
# configure your MTA so that it sends proper sender and
|
46
|
+
# recipient information to gurgitate so that this code never
|
47
|
+
# has to be run at all.
|
48
|
+
def guess_sender
|
49
|
+
# Start by worrying about the "From foo@bar" line. If it's
|
50
|
+
# not there, then make one up from the Return-Path: header.
|
51
|
+
# If there isn't a "Return-Path:" header (then I suspect we
|
52
|
+
# have bigger problems, but still) then use From: as a wild
|
53
|
+
# guess. If I hope that this entire lot of code doesn't get
|
54
|
+
# used, then I _particularly_ hope that things never get so
|
55
|
+
# bad that poor gurgitate has to use the From: header as a
|
56
|
+
# source of authoritative information on anything.
|
57
|
+
#
|
58
|
+
# And then after all that fuss, if we're delivering to a
|
59
|
+
# Maildir, I have to get rid of it. And sometimes the MTA
|
60
|
+
# gives me a mbox-style From line and sometimes it doesn't.
|
61
|
+
# It's annoying, but I have no choice but to Just Deal With
|
62
|
+
# It.
|
63
|
+
if @unix_from then
|
64
|
+
# If it is there, then grab the email address in it and
|
65
|
+
# use that as our official "from".
|
66
|
+
fromregex=/^From ([^ ]+@[^ ]+) /
|
67
|
+
fromregex.match(@unix_from)
|
68
|
+
@from=$+
|
69
|
+
|
70
|
+
# or maybe it's local
|
71
|
+
if @from == nil then
|
72
|
+
@unix_from =~ /^From (\S+) /
|
73
|
+
@from=$+
|
74
|
+
end
|
75
|
+
else
|
76
|
+
fromregex=/([^ ]+@[^ ]+) \(.*\)|[^<]*[<](.*@.*)[>]|([^ ]+@[^ ]+)/
|
77
|
+
if self["Return-Path"] != nil then
|
78
|
+
fromregex.match(self["Return-Path"][0].contents)
|
79
|
+
else
|
80
|
+
if self["From"] != nil then
|
81
|
+
fromregex.match(self["From"][0].contents)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
address_candidate=$+
|
85
|
+
|
86
|
+
# If there STILL isn't a match, then it's probably safe to
|
87
|
+
# assume that it's local mail, and doesn't have an @ in its
|
88
|
+
# address.
|
89
|
+
unless address_candidate
|
90
|
+
if self["Return-Path"] != nil then
|
91
|
+
self["Return-Path"][0].contents =~ /(\S+)/
|
92
|
+
address_candidate=$+
|
93
|
+
else
|
94
|
+
self["From"][0].contents =~ /(\S+)/
|
95
|
+
address_candidate=$+
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
@from=address_candidate
|
100
|
+
|
101
|
+
@unix_from="From "+self.from+" "+Time.new.to_s
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
public
|
106
|
+
|
107
|
+
# Creates a MailHeaders object.
|
108
|
+
# headertext::
|
109
|
+
# The text of the message headers.
|
110
|
+
def initialize(headertext=nil, sender=nil, recipient=nil)
|
111
|
+
@from = sender
|
112
|
+
@to = recipient
|
113
|
+
@headers = Hash.new(nil)
|
114
|
+
|
115
|
+
if Hash === headertext
|
116
|
+
@headers_changed = true
|
117
|
+
headertext.each_key do |key|
|
118
|
+
|
119
|
+
headername = key.to_s.gsub("_","-")
|
120
|
+
|
121
|
+
header=Header.new(headername, headertext[key])
|
122
|
+
@headers[header.name] ||= HeaderBag.new
|
123
|
+
@headers[header.name].push(header)
|
124
|
+
end
|
125
|
+
else
|
126
|
+
if headertext
|
127
|
+
@unix_from, @headertext = figure_out_from_line headertext
|
128
|
+
parse_headers if @headertext
|
129
|
+
|
130
|
+
if sender # then don't believe the mbox separator
|
131
|
+
@from = sender
|
132
|
+
@unix_from="From "+self.from+" "+Time.new.to_s
|
133
|
+
else
|
134
|
+
guess_sender
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
# Who the message is to (the envelope to)
|
141
|
+
#
|
142
|
+
# Yet another bucket of rants. Unix mail sucks.
|
143
|
+
def to
|
144
|
+
return @to || @headers["X-Original-To"] || nil
|
145
|
+
end
|
146
|
+
|
147
|
+
# Who the message is from (the envelope from)
|
148
|
+
def from
|
149
|
+
return @from || ""
|
150
|
+
end
|
151
|
+
|
152
|
+
# Change the envelope from line to whatever you want. This might
|
153
|
+
# not be particularly neighborly, but oh well.
|
154
|
+
# newfrom:: An email address
|
155
|
+
def from=(newfrom)
|
156
|
+
@from=newfrom
|
157
|
+
@unix_from="From "+self.from+" "+Time.new.to_s
|
158
|
+
end
|
159
|
+
|
160
|
+
# Returns the headers properly formatted for an mbox-format
|
161
|
+
# email message.
|
162
|
+
def to_mbox
|
163
|
+
return @unix_from+"\n"+to_s
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|