rumbster 1.0.0
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/COPYING +515 -0
- data/README +56 -0
- data/Rakefile +12 -0
- data/lib/message_observers.rb +40 -0
- data/lib/rumbster.rb +24 -0
- data/lib/smtp_protocol.rb +42 -0
- data/lib/smtp_states.rb +159 -0
- data/test/message_observers_test.rb +102 -0
- data/test/rumbster_test.rb +69 -0
- data/test/smtp_protocol_test.rb +64 -0
- data/test/smtp_states_test.rb +217 -0
- data/vendor/tmail.rb +4 -0
- data/vendor/tmail/.cvsignore +3 -0
- data/vendor/tmail/Makefile +19 -0
- data/vendor/tmail/address.rb +222 -0
- data/vendor/tmail/base64.rb +52 -0
- data/vendor/tmail/compat.rb +39 -0
- data/vendor/tmail/config.rb +50 -0
- data/vendor/tmail/encode.rb +447 -0
- data/vendor/tmail/header.rb +895 -0
- data/vendor/tmail/info.rb +14 -0
- data/vendor/tmail/loader.rb +1 -0
- data/vendor/tmail/mail.rb +869 -0
- data/vendor/tmail/mailbox.rb +386 -0
- data/vendor/tmail/mbox.rb +1 -0
- data/vendor/tmail/net.rb +260 -0
- data/vendor/tmail/obsolete.rb +122 -0
- data/vendor/tmail/parser.rb +1475 -0
- data/vendor/tmail/parser.y +372 -0
- data/vendor/tmail/port.rb +356 -0
- data/vendor/tmail/scanner.rb +22 -0
- data/vendor/tmail/scanner_r.rb +243 -0
- data/vendor/tmail/stringio.rb +256 -0
- data/vendor/tmail/textutils.rb +197 -0
- data/vendor/tmail/tmail.rb +1 -0
- data/vendor/tmail/utils.rb +23 -0
- metadata +88 -0
@@ -0,0 +1,386 @@
|
|
1
|
+
#
|
2
|
+
# mailbox.rb
|
3
|
+
#
|
4
|
+
# Copyright (c) 1998-2004 Minero Aoki
|
5
|
+
#
|
6
|
+
# This program is free software.
|
7
|
+
# You can distribute/modify this program under the terms of
|
8
|
+
# the GNU Lesser General Public License version 2.1.
|
9
|
+
#
|
10
|
+
|
11
|
+
require 'tmail/compat'
|
12
|
+
require 'tmail/port'
|
13
|
+
require 'tmail/textutils'
|
14
|
+
require 'socket'
|
15
|
+
require 'mutex_m'
|
16
|
+
|
17
|
+
module TMail
|
18
|
+
|
19
|
+
class MhMailbox
|
20
|
+
|
21
|
+
PORT_CLASS = MhPort
|
22
|
+
|
23
|
+
def initialize(dir)
|
24
|
+
raise ArgumentError, "not directory: #{dir}" unless File.directory?(dir)
|
25
|
+
@dirname = File.expand_path(dir)
|
26
|
+
@last_file = nil
|
27
|
+
@last_atime = nil
|
28
|
+
end
|
29
|
+
|
30
|
+
def directory
|
31
|
+
@dirname
|
32
|
+
end
|
33
|
+
|
34
|
+
alias dirname directory
|
35
|
+
|
36
|
+
attr_accessor :last_atime
|
37
|
+
|
38
|
+
def inspect
|
39
|
+
"#<#{self.class} #{@dirname}>"
|
40
|
+
end
|
41
|
+
|
42
|
+
def close
|
43
|
+
end
|
44
|
+
|
45
|
+
def new_port
|
46
|
+
PORT_CLASS.new(next_file_name(@dirname))
|
47
|
+
end
|
48
|
+
|
49
|
+
def each_port
|
50
|
+
sorted_mail_entries(@dirname).each do |ent|
|
51
|
+
yield PORT_CLASS.new("#{@dirname}/#{ent}")
|
52
|
+
end
|
53
|
+
@last_atime = Time.now
|
54
|
+
end
|
55
|
+
|
56
|
+
alias each each_port
|
57
|
+
|
58
|
+
def reverse_each_port
|
59
|
+
sorted_mail_entries(@dirname).reverse_each do |ent|
|
60
|
+
yield PORT_CLASS.new("#{@dirname}/#{ent}")
|
61
|
+
end
|
62
|
+
@last_atime = Time.now
|
63
|
+
end
|
64
|
+
|
65
|
+
alias reverse_each reverse_each_port
|
66
|
+
|
67
|
+
# Old #each_mail returns Port, we cannot define this method now.
|
68
|
+
#def each_mail
|
69
|
+
# each_port do |port|
|
70
|
+
# yield Mail.new(port)
|
71
|
+
# end
|
72
|
+
#end
|
73
|
+
|
74
|
+
def each_new_port(mtime = nil, &block)
|
75
|
+
mtime ||= @last_atime
|
76
|
+
return each_port(&block) unless mtime
|
77
|
+
return unless File.mtime(@dirname) >= mtime
|
78
|
+
|
79
|
+
sorted_mail_entries(@dirname).each do |ent|
|
80
|
+
path = "#{@dirname}/#{ent}"
|
81
|
+
yield PORT_CLASS.new(path) if File.mtime(path) > mtime
|
82
|
+
end
|
83
|
+
@last_atime = Time.now
|
84
|
+
end
|
85
|
+
|
86
|
+
private
|
87
|
+
|
88
|
+
def sorted_mail_entries(dir)
|
89
|
+
Dir.entries(dir)\
|
90
|
+
.select {|ent| /\A\d+\z/ =~ ent }\
|
91
|
+
.select {|ent| File.file?("#{dir}/#{ent}") }\
|
92
|
+
.sort_by {|ent| ent.to_i }
|
93
|
+
end
|
94
|
+
|
95
|
+
# This method is not multiprocess safe
|
96
|
+
def next_file_name(dir)
|
97
|
+
n = @last_file
|
98
|
+
n = sorted_mail_entries(dir).last.to_i unless n
|
99
|
+
begin
|
100
|
+
n += 1
|
101
|
+
end while File.exist?("#{dir}/#{n}")
|
102
|
+
@last_file = n
|
103
|
+
"#{@dirname}/#{n}"
|
104
|
+
end
|
105
|
+
|
106
|
+
end # MhMailbox
|
107
|
+
|
108
|
+
MhLoader = MhMailbox
|
109
|
+
|
110
|
+
|
111
|
+
class UNIXMbox
|
112
|
+
|
113
|
+
def UNIXMbox.lock(fname, mode)
|
114
|
+
begin
|
115
|
+
f = File.open(fname, mode)
|
116
|
+
f.flock File::LOCK_EX
|
117
|
+
yield f
|
118
|
+
ensure
|
119
|
+
f.flock File::LOCK_UN
|
120
|
+
f.close if f and not f.closed?
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
class << self
|
125
|
+
alias newobj new
|
126
|
+
include TextUtils
|
127
|
+
end
|
128
|
+
|
129
|
+
def UNIXMbox.new(fname, tmpdir = nil, readonly = false)
|
130
|
+
tmpdir = ENV['TEMP'] || ENV['TMP'] || '/tmp'
|
131
|
+
newobj(fname, "#{tmpdir}/ruby_tmail_#{$$}_#{rand()}", readonly, false)
|
132
|
+
end
|
133
|
+
|
134
|
+
def UNIXMbox.static_new(fname, dir, readonly = false)
|
135
|
+
newobj(fname, dir, readonly, true)
|
136
|
+
end
|
137
|
+
|
138
|
+
def initialize(fname, mhdir, readonly, static)
|
139
|
+
@filename = fname
|
140
|
+
@readonly = readonly
|
141
|
+
@closed = false
|
142
|
+
|
143
|
+
Dir.mkdir mhdir
|
144
|
+
@real = MhMailbox.new(mhdir)
|
145
|
+
@finalizer = UNIXMbox.mkfinal(@real, @filename, !@readonly, !static)
|
146
|
+
ObjectSpace.define_finalizer self, @finalizer
|
147
|
+
end
|
148
|
+
|
149
|
+
def UNIXMbox.mkfinal(mh, mboxfile, writeback_p, cleanup_p)
|
150
|
+
lambda {
|
151
|
+
if writeback_p
|
152
|
+
lock(mboxfile, "r+") {|f|
|
153
|
+
mh.each_port do |port|
|
154
|
+
f.puts create_from_line(port)
|
155
|
+
port.ropen {|r|
|
156
|
+
f.puts r.read
|
157
|
+
}
|
158
|
+
end
|
159
|
+
}
|
160
|
+
end
|
161
|
+
if cleanup_p
|
162
|
+
Dir.foreach(mh.dirname) do |fname|
|
163
|
+
next if /\A\.\.?\z/ =~ fname
|
164
|
+
File.unlink "#{mh.dirname}/#{fname}"
|
165
|
+
end
|
166
|
+
Dir.rmdir mh.dirname
|
167
|
+
end
|
168
|
+
}
|
169
|
+
end
|
170
|
+
|
171
|
+
# make _From line
|
172
|
+
def UNIXMbox.create_from_line(port)
|
173
|
+
sprintf 'From %s %s',
|
174
|
+
fromaddr(port), time2str(File.mtime(port.filename))
|
175
|
+
end
|
176
|
+
|
177
|
+
def UNIXMbox.fromaddr(port)
|
178
|
+
h = HeaderField.new_from_port(port, 'Return-Path') ||
|
179
|
+
HeaderField.new_from_port(port, 'From') or return 'nobody'
|
180
|
+
a = h.addrs[0] or return 'nobody'
|
181
|
+
a.spec
|
182
|
+
end
|
183
|
+
private_class_method :fromaddr
|
184
|
+
|
185
|
+
def close
|
186
|
+
return if @closed
|
187
|
+
|
188
|
+
ObjectSpace.undefine_finalizer self
|
189
|
+
@finalizer.call
|
190
|
+
@finalizer = nil
|
191
|
+
@real = nil
|
192
|
+
@closed = true
|
193
|
+
@updated = nil
|
194
|
+
end
|
195
|
+
|
196
|
+
def each_port(&block)
|
197
|
+
close_check
|
198
|
+
update
|
199
|
+
@real.each_port(&block)
|
200
|
+
end
|
201
|
+
|
202
|
+
alias each each_port
|
203
|
+
|
204
|
+
def reverse_each_port(&block)
|
205
|
+
close_check
|
206
|
+
update
|
207
|
+
@real.reverse_each_port(&block)
|
208
|
+
end
|
209
|
+
|
210
|
+
alias reverse_each reverse_each_port
|
211
|
+
|
212
|
+
# old #each_mail returns Port
|
213
|
+
#def each_mail( &block )
|
214
|
+
# each_port do |port|
|
215
|
+
# yield Mail.new(port)
|
216
|
+
# end
|
217
|
+
#end
|
218
|
+
|
219
|
+
def each_new_port(mtime = nil)
|
220
|
+
close_check
|
221
|
+
update
|
222
|
+
@real.each_new_port(mtime) {|p| yield p }
|
223
|
+
end
|
224
|
+
|
225
|
+
def new_port
|
226
|
+
close_check
|
227
|
+
@real.new_port
|
228
|
+
end
|
229
|
+
|
230
|
+
private
|
231
|
+
|
232
|
+
def close_check
|
233
|
+
@closed and raise ArgumentError, 'accessing already closed mbox'
|
234
|
+
end
|
235
|
+
|
236
|
+
def update
|
237
|
+
return if FileTest.zero?(@filename)
|
238
|
+
return if @updated and File.mtime(@filename) < @updated
|
239
|
+
w = nil
|
240
|
+
port = nil
|
241
|
+
time = nil
|
242
|
+
UNIXMbox.lock(@filename, @readonly ? "r" : "r+") {|f|
|
243
|
+
begin
|
244
|
+
f.each do |line|
|
245
|
+
if /\AFrom / =~ line
|
246
|
+
w.close if w
|
247
|
+
File.utime time, time, port.filename if time
|
248
|
+
port = @real.new_port
|
249
|
+
w = port.wopen
|
250
|
+
time = fromline2time(line)
|
251
|
+
else
|
252
|
+
w.print line if w
|
253
|
+
end
|
254
|
+
end
|
255
|
+
ensure
|
256
|
+
if w and not w.closed?
|
257
|
+
w.close
|
258
|
+
File.utime time, time, port.filename if time
|
259
|
+
end
|
260
|
+
end
|
261
|
+
f.truncate(0) unless @readonly
|
262
|
+
@updated = Time.now
|
263
|
+
}
|
264
|
+
end
|
265
|
+
|
266
|
+
def fromline2time(line)
|
267
|
+
m = /\AFrom \S+ \w+ (\w+) (\d+) (\d+):(\d+):(\d+) (\d+)/.match(line) \
|
268
|
+
or return nil
|
269
|
+
Time.local(m[6].to_i, m[1], m[2].to_i, m[3].to_i, m[4].to_i, m[5].to_i)
|
270
|
+
end
|
271
|
+
|
272
|
+
end # UNIXMbox
|
273
|
+
|
274
|
+
MboxLoader = UNIXMbox
|
275
|
+
|
276
|
+
|
277
|
+
class Maildir
|
278
|
+
|
279
|
+
extend Mutex_m
|
280
|
+
|
281
|
+
PORT_CLASS = MaildirPort
|
282
|
+
|
283
|
+
@seq = 0
|
284
|
+
def Maildir.unique_number
|
285
|
+
synchronize {
|
286
|
+
@seq += 1
|
287
|
+
return @seq
|
288
|
+
}
|
289
|
+
end
|
290
|
+
|
291
|
+
def initialize(dir = nil)
|
292
|
+
@dirname = dir || ENV['MAILDIR']
|
293
|
+
raise ArgumentError, "not directory: #{@dirname}"\
|
294
|
+
unless FileTest.directory?(@dirname)
|
295
|
+
@new = "#{@dirname}/new"
|
296
|
+
@tmp = "#{@dirname}/tmp"
|
297
|
+
@cur = "#{@dirname}/cur"
|
298
|
+
end
|
299
|
+
|
300
|
+
def directory
|
301
|
+
@dirname
|
302
|
+
end
|
303
|
+
|
304
|
+
def inspect
|
305
|
+
"#<#{self.class} #{@dirname}>"
|
306
|
+
end
|
307
|
+
|
308
|
+
def close
|
309
|
+
end
|
310
|
+
|
311
|
+
def each_port
|
312
|
+
sorted_mail_entries(@cur).each do |ent|
|
313
|
+
yield PORT_CLASS.new("#{@cur}/#{ent}")
|
314
|
+
end
|
315
|
+
end
|
316
|
+
|
317
|
+
alias each each_port
|
318
|
+
|
319
|
+
def reverse_each_port
|
320
|
+
sorted_mail_entries(@cur).reverse_each do |ent|
|
321
|
+
yield PORT_CLASS.new("#{@cur}/#{ent}")
|
322
|
+
end
|
323
|
+
end
|
324
|
+
|
325
|
+
alias reverse_each reverse_each_port
|
326
|
+
|
327
|
+
def new_port(&block)
|
328
|
+
fname = nil
|
329
|
+
tmpfname = nil
|
330
|
+
newfname = nil
|
331
|
+
begin
|
332
|
+
fname = "#{Time.now.to_i}.#{$$}_#{Maildir.unique_number}.#{Socket.gethostname}"
|
333
|
+
tmpfname = "#{@tmp}/#{fname}"
|
334
|
+
newfname = "#{@new}/#{fname}"
|
335
|
+
end while FileTest.exist?(tmpfname)
|
336
|
+
|
337
|
+
if block_given?
|
338
|
+
File.open(tmpfname, 'w', &block)
|
339
|
+
File.rename tmpfname, newfname
|
340
|
+
PORT_CLASS.new(newfname)
|
341
|
+
else
|
342
|
+
File.open(tmpfname, 'w') {|f| f.write "\n\n" }
|
343
|
+
PORT_CLASS.new(tmpfname)
|
344
|
+
end
|
345
|
+
end
|
346
|
+
|
347
|
+
def each_new_port
|
348
|
+
sorted_mail_entries(@new).each do |ent|
|
349
|
+
dest = "#{@cur}/#{ent}"
|
350
|
+
File.rename "#{@new}/#{ent}", dest
|
351
|
+
yield PORT_CLASS.new(dest)
|
352
|
+
end
|
353
|
+
check_tmp
|
354
|
+
end
|
355
|
+
|
356
|
+
TOO_OLD = 60 * 60 * 36 # 36 hour
|
357
|
+
|
358
|
+
def check_tmp
|
359
|
+
old = Time.now.to_i - TOO_OLD
|
360
|
+
mail_entries(@tmp).each do |ent|
|
361
|
+
begin
|
362
|
+
path = "#{@tmp}/#{ent}"
|
363
|
+
File.unlink path if File.mtime(path).to_i < old
|
364
|
+
rescue Errno::ENOENT
|
365
|
+
# maybe other process removed
|
366
|
+
end
|
367
|
+
end
|
368
|
+
end
|
369
|
+
|
370
|
+
private
|
371
|
+
|
372
|
+
def sorted_mail_entries(dir)
|
373
|
+
mail_entries(dir).sort_by {|ent| ent.slice(/\A\d+/).to_i }
|
374
|
+
end
|
375
|
+
|
376
|
+
def mail_entries(dir)
|
377
|
+
Dir.entries(dir)\
|
378
|
+
.reject {|ent| /\A\./ =~ ent }\
|
379
|
+
.select {|ent| File.file?("#{dir}/#{ent}") }
|
380
|
+
end
|
381
|
+
|
382
|
+
end # Maildir
|
383
|
+
|
384
|
+
MaildirLoader = Maildir
|
385
|
+
|
386
|
+
end # module TMail
|
@@ -0,0 +1 @@
|
|
1
|
+
require 'tmail/mailbox'
|
data/vendor/tmail/net.rb
ADDED
@@ -0,0 +1,260 @@
|
|
1
|
+
#
|
2
|
+
# net.rb
|
3
|
+
#
|
4
|
+
# Copyright (c) 1998-2004 Minero Aoki
|
5
|
+
#
|
6
|
+
# This program is free software.
|
7
|
+
# You can distribute/modify this program under the terms of
|
8
|
+
# the GNU Lesser General Public License version 2.1.
|
9
|
+
#
|
10
|
+
|
11
|
+
require 'nkf'
|
12
|
+
|
13
|
+
module TMail
|
14
|
+
|
15
|
+
class Mail
|
16
|
+
|
17
|
+
def send_to(smtp)
|
18
|
+
do_send_to(smtp) do
|
19
|
+
ready_to_send
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def send_text_to(smtp)
|
24
|
+
do_send_to(smtp) do
|
25
|
+
ready_to_send
|
26
|
+
mime_encode
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def do_send_to(smtp)
|
31
|
+
from = from_address or raise ArgumentError, 'no from address'
|
32
|
+
(dests = destinations).empty? and raise ArgumentError, 'no receipient'
|
33
|
+
yield
|
34
|
+
send_to_0 smtp, from, dests
|
35
|
+
end
|
36
|
+
private :do_send_to
|
37
|
+
|
38
|
+
def send_to_0(smtp, from, to)
|
39
|
+
smtp.ready(from, to) do |f|
|
40
|
+
encoded "\r\n", 'j', f, ''
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def ready_to_send
|
45
|
+
delete_no_send_fields
|
46
|
+
add_message_id
|
47
|
+
add_date
|
48
|
+
end
|
49
|
+
|
50
|
+
NOSEND_FIELDS = %w(
|
51
|
+
received
|
52
|
+
bcc
|
53
|
+
)
|
54
|
+
|
55
|
+
def delete_no_send_fields
|
56
|
+
NOSEND_FIELDS.each do |nm|
|
57
|
+
delete nm
|
58
|
+
end
|
59
|
+
delete_if {|n,v| v.empty? }
|
60
|
+
end
|
61
|
+
|
62
|
+
def add_message_id(fqdn = nil)
|
63
|
+
self.message_id = ::TMail::new_msgid(fqdn)
|
64
|
+
end
|
65
|
+
|
66
|
+
def add_date
|
67
|
+
self.date = Time.now
|
68
|
+
end
|
69
|
+
|
70
|
+
def mime_encode
|
71
|
+
if parts.empty?
|
72
|
+
mime_encode_singlepart
|
73
|
+
else
|
74
|
+
mime_encode_multipart true
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def mime_encode_singlepart
|
79
|
+
self.mime_version = '1.0'
|
80
|
+
b = body
|
81
|
+
if NKF.guess(b) != NKF::BINARY
|
82
|
+
mime_encode_text b
|
83
|
+
else
|
84
|
+
mime_encode_binary b
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def mime_encode_text(body)
|
89
|
+
self.body = NKF.nkf('-j -m0', body)
|
90
|
+
self.set_content_type 'text', 'plain', {'charset' => 'iso-2022-jp'}
|
91
|
+
self.encoding = '7bit'
|
92
|
+
end
|
93
|
+
|
94
|
+
def mime_encode_binary(body)
|
95
|
+
self.body = [body].pack('m')
|
96
|
+
self.set_content_type 'application', 'octet-stream'
|
97
|
+
self.encoding = 'Base64'
|
98
|
+
end
|
99
|
+
|
100
|
+
def mime_encode_multipart(top = true)
|
101
|
+
self.mime_version = '1.0' if top
|
102
|
+
self.set_content_type 'multipart', 'mixed'
|
103
|
+
e = encoding(nil)
|
104
|
+
if e and not /\A(?:7bit|8bit|binary)\z/i =~ e
|
105
|
+
raise ArgumentError,
|
106
|
+
'using C.T.Encoding with multipart mail is not permitted'
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
def create_empty_mail
|
111
|
+
self.class.new(StringPort.new(''), @config)
|
112
|
+
end
|
113
|
+
|
114
|
+
def create_reply
|
115
|
+
setup_reply create_empty_mail()
|
116
|
+
end
|
117
|
+
|
118
|
+
def setup_reply(m)
|
119
|
+
if tmp = reply_addresses(nil)
|
120
|
+
m.to_addrs = tmp
|
121
|
+
end
|
122
|
+
|
123
|
+
mid = message_id(nil)
|
124
|
+
tmp = references(nil) || []
|
125
|
+
tmp.push mid if mid
|
126
|
+
m.in_reply_to = [mid] if mid
|
127
|
+
m.references = tmp unless tmp.empty?
|
128
|
+
m.subject = 'Re: ' + subject('').sub(/\A(?:\s*re:)+/i, '')
|
129
|
+
|
130
|
+
m
|
131
|
+
end
|
132
|
+
|
133
|
+
def create_forward
|
134
|
+
setup_forward create_empty_mail()
|
135
|
+
end
|
136
|
+
|
137
|
+
def setup_forward(mail)
|
138
|
+
m = Mail.new(StringPort.new(''))
|
139
|
+
m.body = decoded
|
140
|
+
m.set_content_type 'message', 'rfc822'
|
141
|
+
m.encoding = encoding('7bit')
|
142
|
+
mail.parts.push m
|
143
|
+
end
|
144
|
+
|
145
|
+
end
|
146
|
+
|
147
|
+
|
148
|
+
class DeleteFields
|
149
|
+
|
150
|
+
NOSEND_FIELDS = %w(
|
151
|
+
received
|
152
|
+
bcc
|
153
|
+
)
|
154
|
+
|
155
|
+
def initialize(nosend = nil, delempty = true)
|
156
|
+
@no_send_fields = nosend || NOSEND_FIELDS.dup
|
157
|
+
@delete_empty_fields = delempty
|
158
|
+
end
|
159
|
+
|
160
|
+
attr :no_send_fields
|
161
|
+
attr :delete_empty_fields, true
|
162
|
+
|
163
|
+
def exec(mail)
|
164
|
+
@no_send_fields.each do |nm|
|
165
|
+
delete nm
|
166
|
+
end
|
167
|
+
delete_if {|n,v| v.empty? } if @delete_empty_fields
|
168
|
+
end
|
169
|
+
|
170
|
+
end
|
171
|
+
|
172
|
+
|
173
|
+
class AddMessageId
|
174
|
+
|
175
|
+
def initialize(fqdn = nil)
|
176
|
+
@fqdn = fqdn
|
177
|
+
end
|
178
|
+
|
179
|
+
attr :fqdn, true
|
180
|
+
|
181
|
+
def exec(mail)
|
182
|
+
mail.message_id = ::TMail::new_msgid(@fqdn)
|
183
|
+
end
|
184
|
+
|
185
|
+
end
|
186
|
+
|
187
|
+
|
188
|
+
class AddDate
|
189
|
+
|
190
|
+
def exec(mail)
|
191
|
+
mail.date = Time.now
|
192
|
+
end
|
193
|
+
|
194
|
+
end
|
195
|
+
|
196
|
+
|
197
|
+
class MimeEncodeAuto
|
198
|
+
|
199
|
+
def initialize(s = nil, m = nil)
|
200
|
+
@singlepart_composer = s || MimeEncodeSingle.new
|
201
|
+
@multipart_composer = m || MimeEncodeMulti.new
|
202
|
+
end
|
203
|
+
|
204
|
+
attr :singlepart_composer
|
205
|
+
attr :multipart_composer
|
206
|
+
|
207
|
+
def exec(mail)
|
208
|
+
if mail._builtin_multipart?
|
209
|
+
then @multipart_composer
|
210
|
+
else @singlepart_composer end.exec mail
|
211
|
+
end
|
212
|
+
|
213
|
+
end
|
214
|
+
|
215
|
+
|
216
|
+
class MimeEncodeSingle
|
217
|
+
|
218
|
+
def exec(mail)
|
219
|
+
mail.mime_version = '1.0'
|
220
|
+
b = mail.body
|
221
|
+
if NKF.guess(b) != NKF::BINARY
|
222
|
+
on_text b
|
223
|
+
else
|
224
|
+
on_binary b
|
225
|
+
end
|
226
|
+
end
|
227
|
+
|
228
|
+
def on_text(body)
|
229
|
+
mail.body = NKF.nkf('-j -m0', body)
|
230
|
+
mail.set_content_type 'text', 'plain', {'charset' => 'iso-2022-jp'}
|
231
|
+
mail.encoding = '7bit'
|
232
|
+
end
|
233
|
+
|
234
|
+
def on_binary(body)
|
235
|
+
mail.body = [body].pack('m')
|
236
|
+
mail.set_content_type 'application', 'octet-stream'
|
237
|
+
mail.encoding = 'Base64'
|
238
|
+
end
|
239
|
+
|
240
|
+
end
|
241
|
+
|
242
|
+
|
243
|
+
class MimeEncodeMulti
|
244
|
+
|
245
|
+
def exec(mail, top = true)
|
246
|
+
mail.mime_version = '1.0' if top
|
247
|
+
mail.set_content_type 'multipart', 'mixed'
|
248
|
+
e = encoding(nil)
|
249
|
+
if e and not /\A(?:7bit|8bit|binary)\z/i =~ e
|
250
|
+
raise ArgumentError,
|
251
|
+
'using C.T.Encoding with multipart mail is not permitted'
|
252
|
+
end
|
253
|
+
mail.parts.each do |m|
|
254
|
+
exec m, false if m._builtin_multipart?
|
255
|
+
end
|
256
|
+
end
|
257
|
+
|
258
|
+
end
|
259
|
+
|
260
|
+
end # module TMail
|