tmail 1.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (102) hide show
  1. data/LICENSE +21 -0
  2. data/README +157 -0
  3. data/bat/changelog +19 -0
  4. data/bat/clobber/package +10 -0
  5. data/bat/compile +42 -0
  6. data/bat/config.yaml +8 -0
  7. data/bat/prepare +8 -0
  8. data/bat/publish +51 -0
  9. data/bat/rdoc +42 -0
  10. data/bat/release +12 -0
  11. data/bat/setup +1616 -0
  12. data/bat/stats +138 -0
  13. data/bat/tag +25 -0
  14. data/bat/test +25 -0
  15. data/ext/tmail/Makefile +25 -0
  16. data/ext/tmail/base64/MANIFEST +4 -0
  17. data/ext/tmail/base64/base64.c +264 -0
  18. data/ext/tmail/base64/depend +1 -0
  19. data/ext/tmail/base64/extconf.rb +38 -0
  20. data/ext/tmail/scanner_c/MANIFEST +4 -0
  21. data/ext/tmail/scanner_c/depend +1 -0
  22. data/ext/tmail/scanner_c/extconf.rb +38 -0
  23. data/ext/tmail/scanner_c/scanner_c.c +582 -0
  24. data/lib/tmail.rb +4 -0
  25. data/lib/tmail/Makefile +19 -0
  26. data/lib/tmail/address.rb +245 -0
  27. data/lib/tmail/attachments.rb +47 -0
  28. data/lib/tmail/base64.rb +75 -0
  29. data/lib/tmail/compat.rb +39 -0
  30. data/lib/tmail/config.rb +71 -0
  31. data/lib/tmail/core_extensions.rb +67 -0
  32. data/lib/tmail/encode.rb +524 -0
  33. data/lib/tmail/header.rb +931 -0
  34. data/lib/tmail/index.rb +8 -0
  35. data/lib/tmail/interface.rb +540 -0
  36. data/lib/tmail/loader.rb +1 -0
  37. data/lib/tmail/mail.rb +507 -0
  38. data/lib/tmail/mailbox.rb +435 -0
  39. data/lib/tmail/mbox.rb +1 -0
  40. data/lib/tmail/net.rb +282 -0
  41. data/lib/tmail/obsolete.rb +137 -0
  42. data/lib/tmail/parser.rb +1475 -0
  43. data/lib/tmail/parser.y +381 -0
  44. data/lib/tmail/port.rb +379 -0
  45. data/lib/tmail/quoting.rb +142 -0
  46. data/lib/tmail/require_arch.rb +56 -0
  47. data/lib/tmail/scanner.rb +44 -0
  48. data/lib/tmail/scanner_r.rb +263 -0
  49. data/lib/tmail/stringio.rb +279 -0
  50. data/lib/tmail/tmail.rb +1 -0
  51. data/lib/tmail/utils.rb +281 -0
  52. data/lib/tmail/version.rb +38 -0
  53. data/meta/icli.yaml +16 -0
  54. data/meta/tmail-1.1.1.roll +24 -0
  55. data/sample/data/multipart +23 -0
  56. data/sample/data/normal +29 -0
  57. data/sample/data/sendtest +5 -0
  58. data/sample/data/simple +14 -0
  59. data/sample/data/test +27 -0
  60. data/sample/extract-attachements.rb +33 -0
  61. data/sample/from-check.rb +26 -0
  62. data/sample/multipart.rb +26 -0
  63. data/sample/parse-bench.rb +68 -0
  64. data/sample/parse-test.rb +19 -0
  65. data/sample/sendmail.rb +94 -0
  66. data/test/extctrl.rb +6 -0
  67. data/test/fixtures/raw_base64_decoded_string +0 -0
  68. data/test/fixtures/raw_base64_email +83 -0
  69. data/test/fixtures/raw_base64_encoded_string +1 -0
  70. data/test/fixtures/raw_email +14 -0
  71. data/test/fixtures/raw_email10 +20 -0
  72. data/test/fixtures/raw_email11 +34 -0
  73. data/test/fixtures/raw_email12 +32 -0
  74. data/test/fixtures/raw_email13 +29 -0
  75. data/test/fixtures/raw_email2 +114 -0
  76. data/test/fixtures/raw_email3 +70 -0
  77. data/test/fixtures/raw_email4 +59 -0
  78. data/test/fixtures/raw_email5 +19 -0
  79. data/test/fixtures/raw_email6 +20 -0
  80. data/test/fixtures/raw_email7 +66 -0
  81. data/test/fixtures/raw_email8 +47 -0
  82. data/test/fixtures/raw_email9 +28 -0
  83. data/test/fixtures/raw_email_quoted_with_0d0a +14 -0
  84. data/test/fixtures/raw_email_simple +11 -0
  85. data/test/fixtures/raw_email_with_illegal_boundary +58 -0
  86. data/test/fixtures/raw_email_with_multipart_mixed_quoted_boundary +50 -0
  87. data/test/fixtures/raw_email_with_nested_attachment +100 -0
  88. data/test/fixtures/raw_email_with_partially_quoted_subject +14 -0
  89. data/test/fixtures/raw_email_with_quoted_illegal_boundary +58 -0
  90. data/test/kcode.rb +14 -0
  91. data/test/test_address.rb +1128 -0
  92. data/test/test_attachments.rb +35 -0
  93. data/test/test_base64.rb +63 -0
  94. data/test/test_encode.rb +77 -0
  95. data/test/test_header.rb +885 -0
  96. data/test/test_helper.rb +2 -0
  97. data/test/test_mail.rb +623 -0
  98. data/test/test_mbox.rb +126 -0
  99. data/test/test_port.rb +430 -0
  100. data/test/test_scanner.rb +209 -0
  101. data/test/test_utils.rb +37 -0
  102. metadata +205 -0
@@ -0,0 +1,435 @@
1
+ =begin rdoc
2
+
3
+ = Mailbox and Mbox interaction class
4
+
5
+ =end
6
+ #--
7
+ # Copyright (c) 1998-2003 Minero Aoki <aamine@loveruby.net>
8
+ #
9
+ # Permission is hereby granted, free of charge, to any person obtaining
10
+ # a copy of this software and associated documentation files (the
11
+ # "Software"), to deal in the Software without restriction, including
12
+ # without limitation the rights to use, copy, modify, merge, publish,
13
+ # distribute, sublicense, and/or sell copies of the Software, and to
14
+ # permit persons to whom the Software is furnished to do so, subject to
15
+ # the following conditions:
16
+ #
17
+ # The above copyright notice and this permission notice shall be
18
+ # included in all copies or substantial portions of the Software.
19
+ #
20
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27
+ #
28
+ # Note: Originally licensed under LGPL v2+. Using MIT license for Rails
29
+ # with permission of Minero Aoki.
30
+ #++
31
+
32
+ require 'tmail/port'
33
+ require 'socket'
34
+ require 'mutex_m'
35
+
36
+
37
+ unless [].respond_to?(:sort_by)
38
+ module Enumerable#:nodoc:
39
+ def sort_by
40
+ map {|i| [yield(i), i] }.sort {|a,b| a.first <=> b.first }.map {|i| i[1] }
41
+ end
42
+ end
43
+ end
44
+
45
+
46
+ module TMail
47
+
48
+ class MhMailbox
49
+
50
+ PORT_CLASS = MhPort
51
+
52
+ def initialize( dir )
53
+ edir = File.expand_path(dir)
54
+ raise ArgumentError, "not directory: #{dir}"\
55
+ unless FileTest.directory? edir
56
+ @dirname = edir
57
+ @last_file = nil
58
+ @last_atime = nil
59
+ end
60
+
61
+ def directory
62
+ @dirname
63
+ end
64
+
65
+ alias dirname directory
66
+
67
+ attr_accessor :last_atime
68
+
69
+ def inspect
70
+ "#<#{self.class} #{@dirname}>"
71
+ end
72
+
73
+ def close
74
+ end
75
+
76
+ def new_port
77
+ PORT_CLASS.new(next_file_name())
78
+ end
79
+
80
+ def each_port
81
+ mail_files().each do |path|
82
+ yield PORT_CLASS.new(path)
83
+ end
84
+ @last_atime = Time.now
85
+ end
86
+
87
+ alias each each_port
88
+
89
+ def reverse_each_port
90
+ mail_files().reverse_each do |path|
91
+ yield PORT_CLASS.new(path)
92
+ end
93
+ @last_atime = Time.now
94
+ end
95
+
96
+ alias reverse_each reverse_each_port
97
+
98
+ # old #each_mail returns Port
99
+ #def each_mail
100
+ # each_port do |port|
101
+ # yield Mail.new(port)
102
+ # end
103
+ #end
104
+
105
+ def each_new_port( mtime = nil, &block )
106
+ mtime ||= @last_atime
107
+ return each_port(&block) unless mtime
108
+ return unless File.mtime(@dirname) >= mtime
109
+
110
+ mail_files().each do |path|
111
+ yield PORT_CLASS.new(path) if File.mtime(path) > mtime
112
+ end
113
+ @last_atime = Time.now
114
+ end
115
+
116
+ private
117
+
118
+ def mail_files
119
+ Dir.entries(@dirname)\
120
+ .select {|s| /\A\d+\z/ === s }\
121
+ .map {|s| s.to_i }\
122
+ .sort\
123
+ .map {|i| "#{@dirname}/#{i}" }\
124
+ .select {|path| FileTest.file? path }
125
+ end
126
+
127
+ def next_file_name
128
+ unless n = @last_file
129
+ n = 0
130
+ Dir.entries(@dirname)\
131
+ .select {|s| /\A\d+\z/ === s }\
132
+ .map {|s| s.to_i }.sort\
133
+ .each do |i|
134
+ next unless FileTest.file? "#{@dirname}/#{i}"
135
+ n = i
136
+ end
137
+ end
138
+ begin
139
+ n += 1
140
+ end while FileTest.exist? "#{@dirname}/#{n}"
141
+ @last_file = n
142
+
143
+ "#{@dirname}/#{n}"
144
+ end
145
+
146
+ end # MhMailbox
147
+
148
+ MhLoader = MhMailbox
149
+
150
+
151
+ class UNIXMbox
152
+
153
+ def UNIXMbox.lock( fname )
154
+ begin
155
+ f = File.open(fname)
156
+ f.flock File::LOCK_EX
157
+ yield f
158
+ ensure
159
+ f.flock File::LOCK_UN
160
+ f.close if f and not f.closed?
161
+ end
162
+ end
163
+
164
+ class << self
165
+ alias newobj new
166
+ end
167
+
168
+ def UNIXMbox.new( fname, tmpdir = nil, readonly = false )
169
+ tmpdir = ENV['TEMP'] || ENV['TMP'] || '/tmp'
170
+ newobj(fname, "#{tmpdir}/ruby_tmail_#{$$}_#{rand()}", readonly, false)
171
+ end
172
+
173
+ def UNIXMbox.static_new( fname, dir, readonly = false )
174
+ newobj(fname, dir, readonly, true)
175
+ end
176
+
177
+ def initialize( fname, mhdir, readonly, static )
178
+ @filename = fname
179
+ @readonly = readonly
180
+ @closed = false
181
+
182
+ Dir.mkdir mhdir
183
+ @real = MhMailbox.new(mhdir)
184
+ @finalizer = UNIXMbox.mkfinal(@real, @filename, !@readonly, !static)
185
+ ObjectSpace.define_finalizer self, @finalizer
186
+ end
187
+
188
+ def UNIXMbox.mkfinal( mh, mboxfile, writeback_p, cleanup_p )
189
+ lambda {
190
+ if writeback_p
191
+ lock(mboxfile) {|f|
192
+ mh.each_port do |port|
193
+ f.puts create_from_line(port)
194
+ port.ropen {|r|
195
+ f.puts r.read
196
+ }
197
+ end
198
+ }
199
+ end
200
+ if cleanup_p
201
+ Dir.foreach(mh.dirname) do |fname|
202
+ next if /\A\.\.?\z/ === fname
203
+ File.unlink "#{mh.dirname}/#{fname}"
204
+ end
205
+ Dir.rmdir mh.dirname
206
+ end
207
+ }
208
+ end
209
+
210
+ # make _From line
211
+ def UNIXMbox.create_from_line( port )
212
+ sprintf 'From %s %s',
213
+ fromaddr(), TextUtils.time2str(File.mtime(port.filename))
214
+ end
215
+
216
+ def UNIXMbox.fromaddr
217
+ h = HeaderField.new_from_port(port, 'Return-Path') ||
218
+ HeaderField.new_from_port(port, 'From') or return 'nobody'
219
+ a = h.addrs[0] or return 'nobody'
220
+ a.spec
221
+ end
222
+ private_class_method :fromaddr
223
+
224
+ def close
225
+ return if @closed
226
+
227
+ ObjectSpace.undefine_finalizer self
228
+ @finalizer.call
229
+ @finalizer = nil
230
+ @real = nil
231
+ @closed = true
232
+ @updated = nil
233
+ end
234
+
235
+ def each_port( &block )
236
+ close_check
237
+ update
238
+ @real.each_port(&block)
239
+ end
240
+
241
+ alias each each_port
242
+
243
+ def reverse_each_port( &block )
244
+ close_check
245
+ update
246
+ @real.reverse_each_port(&block)
247
+ end
248
+
249
+ alias reverse_each reverse_each_port
250
+
251
+ # old #each_mail returns Port
252
+ #def each_mail( &block )
253
+ # each_port do |port|
254
+ # yield Mail.new(port)
255
+ # end
256
+ #end
257
+
258
+ def each_new_port( mtime = nil )
259
+ close_check
260
+ update
261
+ @real.each_new_port(mtime) {|p| yield p }
262
+ end
263
+
264
+ def new_port
265
+ close_check
266
+ @real.new_port
267
+ end
268
+
269
+ private
270
+
271
+ def close_check
272
+ @closed and raise ArgumentError, 'accessing already closed mbox'
273
+ end
274
+
275
+ def update
276
+ return if FileTest.zero?(@filename)
277
+ return if @updated and File.mtime(@filename) < @updated
278
+ w = nil
279
+ port = nil
280
+ time = nil
281
+ UNIXMbox.lock(@filename) {|f|
282
+ begin
283
+ f.each do |line|
284
+ if /\AFrom / === line
285
+ w.close if w
286
+ File.utime time, time, port.filename if time
287
+
288
+ port = @real.new_port
289
+ w = port.wopen
290
+ time = fromline2time(line)
291
+ else
292
+ w.print line if w
293
+ end
294
+ end
295
+ ensure
296
+ if w and not w.closed?
297
+ w.close
298
+ File.utime time, time, port.filename if time
299
+ end
300
+ end
301
+ f.truncate(0) unless @readonly
302
+ @updated = Time.now
303
+ }
304
+ end
305
+
306
+ def fromline2time( line )
307
+ m = /\AFrom \S+ \w+ (\w+) (\d+) (\d+):(\d+):(\d+) (\d+)/.match(line) \
308
+ or return nil
309
+ Time.local(m[6].to_i, m[1], m[2].to_i, m[3].to_i, m[4].to_i, m[5].to_i)
310
+ end
311
+
312
+ end # UNIXMbox
313
+
314
+ MboxLoader = UNIXMbox
315
+
316
+
317
+ class Maildir
318
+
319
+ extend Mutex_m
320
+
321
+ PORT_CLASS = MaildirPort
322
+
323
+ @seq = 0
324
+ def Maildir.unique_number
325
+ synchronize {
326
+ @seq += 1
327
+ return @seq
328
+ }
329
+ end
330
+
331
+ def initialize( dir = nil )
332
+ @dirname = dir || ENV['MAILDIR']
333
+ raise ArgumentError, "not directory: #{@dirname}"\
334
+ unless FileTest.directory? @dirname
335
+ @new = "#{@dirname}/new"
336
+ @tmp = "#{@dirname}/tmp"
337
+ @cur = "#{@dirname}/cur"
338
+ end
339
+
340
+ def directory
341
+ @dirname
342
+ end
343
+
344
+ def inspect
345
+ "#<#{self.class} #{@dirname}>"
346
+ end
347
+
348
+ def close
349
+ end
350
+
351
+ def each_port
352
+ mail_files(@cur).each do |path|
353
+ yield PORT_CLASS.new(path)
354
+ end
355
+ end
356
+
357
+ alias each each_port
358
+
359
+ def reverse_each_port
360
+ mail_files(@cur).reverse_each do |path|
361
+ yield PORT_CLASS.new(path)
362
+ end
363
+ end
364
+
365
+ alias reverse_each reverse_each_port
366
+
367
+ def new_port
368
+ fname = nil
369
+ tmpfname = nil
370
+ newfname = nil
371
+
372
+ begin
373
+ fname = "#{Time.now.to_i}.#{$$}_#{Maildir.unique_number}.#{Socket.gethostname}"
374
+
375
+ tmpfname = "#{@tmp}/#{fname}"
376
+ newfname = "#{@new}/#{fname}"
377
+ end while FileTest.exist? tmpfname
378
+
379
+ if block_given?
380
+ File.open(tmpfname, 'w') {|f| yield f }
381
+ File.rename tmpfname, newfname
382
+ PORT_CLASS.new(newfname)
383
+ else
384
+ File.open(tmpfname, 'w') {|f| f.write "\n\n" }
385
+ PORT_CLASS.new(tmpfname)
386
+ end
387
+ end
388
+
389
+ def each_new_port
390
+ mail_files(@new).each do |path|
391
+ dest = @cur + '/' + File.basename(path)
392
+ File.rename path, dest
393
+ yield PORT_CLASS.new(dest)
394
+ end
395
+
396
+ check_tmp
397
+ end
398
+
399
+ TOO_OLD = 60 * 60 * 36 # 36 hour
400
+
401
+ def check_tmp
402
+ old = Time.now.to_i - TOO_OLD
403
+
404
+ each_filename(@tmp) do |full, fname|
405
+ if FileTest.file? full and
406
+ File.stat(full).mtime.to_i < old
407
+ File.unlink full
408
+ end
409
+ end
410
+ end
411
+
412
+ private
413
+
414
+ def mail_files( dir )
415
+ Dir.entries(dir)\
416
+ .select {|s| s[0] != ?. }\
417
+ .sort_by {|s| s.slice(/\A\d+/).to_i }\
418
+ .map {|s| "#{dir}/#{s}" }\
419
+ .select {|path| FileTest.file? path }
420
+ end
421
+
422
+ def each_filename( dir )
423
+ Dir.foreach(dir) do |fname|
424
+ path = "#{dir}/#{fname}"
425
+ if fname[0] != ?. and FileTest.file? path
426
+ yield path, fname
427
+ end
428
+ end
429
+ end
430
+
431
+ end # Maildir
432
+
433
+ MaildirLoader = Maildir
434
+
435
+ end # module TMail
@@ -0,0 +1 @@
1
+ require 'tmail/mailbox'
@@ -0,0 +1,282 @@
1
+ =begin rdoc
2
+
3
+ = Net provides SMTP wrapping
4
+
5
+ =end
6
+ #--
7
+ # Copyright (c) 1998-2003 Minero Aoki <aamine@loveruby.net>
8
+ #
9
+ # Permission is hereby granted, free of charge, to any person obtaining
10
+ # a copy of this software and associated documentation files (the
11
+ # "Software"), to deal in the Software without restriction, including
12
+ # without limitation the rights to use, copy, modify, merge, publish,
13
+ # distribute, sublicense, and/or sell copies of the Software, and to
14
+ # permit persons to whom the Software is furnished to do so, subject to
15
+ # the following conditions:
16
+ #
17
+ # The above copyright notice and this permission notice shall be
18
+ # included in all copies or substantial portions of the Software.
19
+ #
20
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27
+ #
28
+ # Note: Originally licensed under LGPL v2+. Using MIT license for Rails
29
+ # with permission of Minero Aoki.
30
+ #++
31
+
32
+ require 'nkf'
33
+
34
+
35
+ module TMail
36
+
37
+ class Mail
38
+
39
+ def send_to( smtp )
40
+ do_send_to(smtp) do
41
+ ready_to_send
42
+ end
43
+ end
44
+
45
+ def send_text_to( smtp )
46
+ do_send_to(smtp) do
47
+ ready_to_send
48
+ mime_encode
49
+ end
50
+ end
51
+
52
+ def do_send_to( smtp )
53
+ from = from_address or raise ArgumentError, 'no from address'
54
+ (dests = destinations).empty? and raise ArgumentError, 'no receipient'
55
+ yield
56
+ send_to_0 smtp, from, dests
57
+ end
58
+ private :do_send_to
59
+
60
+ def send_to_0( smtp, from, to )
61
+ smtp.ready(from, to) do |f|
62
+ encoded "\r\n", 'j', f, ''
63
+ end
64
+ end
65
+
66
+ def ready_to_send
67
+ delete_no_send_fields
68
+ add_message_id
69
+ add_date
70
+ end
71
+
72
+ NOSEND_FIELDS = %w(
73
+ received
74
+ bcc
75
+ )
76
+
77
+ def delete_no_send_fields
78
+ NOSEND_FIELDS.each do |nm|
79
+ delete nm
80
+ end
81
+ delete_if {|n,v| v.empty? }
82
+ end
83
+
84
+ def add_message_id( fqdn = nil )
85
+ self.message_id = ::TMail::new_message_id(fqdn)
86
+ end
87
+
88
+ def add_date
89
+ self.date = Time.now
90
+ end
91
+
92
+ def mime_encode
93
+ if parts.empty?
94
+ mime_encode_singlepart
95
+ else
96
+ mime_encode_multipart true
97
+ end
98
+ end
99
+
100
+ def mime_encode_singlepart
101
+ self.mime_version = '1.0'
102
+ b = body
103
+ if NKF.guess(b) != NKF::BINARY
104
+ mime_encode_text b
105
+ else
106
+ mime_encode_binary b
107
+ end
108
+ end
109
+
110
+ def mime_encode_text( body )
111
+ self.body = NKF.nkf('-j -m0', body)
112
+ self.set_content_type 'text', 'plain', {'charset' => 'iso-2022-jp'}
113
+ self.encoding = '7bit'
114
+ end
115
+
116
+ def mime_encode_binary( body )
117
+ self.body = [body].pack('m')
118
+ self.set_content_type 'application', 'octet-stream'
119
+ self.encoding = 'Base64'
120
+ end
121
+
122
+ def mime_encode_multipart( top = true )
123
+ self.mime_version = '1.0' if top
124
+ self.set_content_type 'multipart', 'mixed'
125
+ e = encoding(nil)
126
+ if e and not /\A(?:7bit|8bit|binary)\z/i === e
127
+ raise ArgumentError,
128
+ 'using C.T.Encoding with multipart mail is not permitted'
129
+ end
130
+ end
131
+
132
+ def create_empty_mail
133
+ self.class.new(StringPort.new(''), @config)
134
+ end
135
+
136
+ def create_reply
137
+ setup_reply create_empty_mail()
138
+ end
139
+
140
+ def setup_reply( m )
141
+ if tmp = reply_addresses(nil)
142
+ m.to_addrs = tmp
143
+ end
144
+
145
+ mid = message_id(nil)
146
+ tmp = references(nil) || []
147
+ tmp.push mid if mid
148
+ m.in_reply_to = [mid] if mid
149
+ m.references = tmp unless tmp.empty?
150
+ m.subject = 'Re: ' + subject('').sub(/\A(?:\s*re:)+/i, '')
151
+
152
+ m
153
+ end
154
+
155
+ def create_forward
156
+ setup_forward create_empty_mail()
157
+ end
158
+
159
+ def setup_forward( mail )
160
+ m = Mail.new(StringPort.new(''))
161
+ m.body = decoded
162
+ m.set_content_type 'message', 'rfc822'
163
+ m.encoding = encoding('7bit')
164
+ mail.parts.push m
165
+ end
166
+
167
+ end
168
+
169
+
170
+ class DeleteFields
171
+
172
+ NOSEND_FIELDS = %w(
173
+ received
174
+ bcc
175
+ )
176
+
177
+ def initialize( nosend = nil, delempty = true )
178
+ @no_send_fields = nosend || NOSEND_FIELDS.dup
179
+ @delete_empty_fields = delempty
180
+ end
181
+
182
+ attr :no_send_fields
183
+ attr :delete_empty_fields, true
184
+
185
+ def exec( mail )
186
+ @no_send_fields.each do |nm|
187
+ delete nm
188
+ end
189
+ delete_if {|n,v| v.empty? } if @delete_empty_fields
190
+ end
191
+
192
+ end
193
+
194
+
195
+ class AddMessageId
196
+
197
+ def initialize( fqdn = nil )
198
+ @fqdn = fqdn
199
+ end
200
+
201
+ attr :fqdn, true
202
+
203
+ def exec( mail )
204
+ mail.message_id = ::TMail::new_msgid(@fqdn)
205
+ end
206
+
207
+ end
208
+
209
+
210
+ class AddDate
211
+
212
+ def exec( mail )
213
+ mail.date = Time.now
214
+ end
215
+
216
+ end
217
+
218
+
219
+ class MimeEncodeAuto
220
+
221
+ def initialize( s = nil, m = nil )
222
+ @singlepart_composer = s || MimeEncodeSingle.new
223
+ @multipart_composer = m || MimeEncodeMulti.new
224
+ end
225
+
226
+ attr :singlepart_composer
227
+ attr :multipart_composer
228
+
229
+ def exec( mail )
230
+ if mail._builtin_multipart?
231
+ then @multipart_composer
232
+ else @singlepart_composer end.exec mail
233
+ end
234
+
235
+ end
236
+
237
+
238
+ class MimeEncodeSingle
239
+
240
+ def exec( mail )
241
+ mail.mime_version = '1.0'
242
+ b = mail.body
243
+ if NKF.guess(b) != NKF::BINARY
244
+ on_text b
245
+ else
246
+ on_binary b
247
+ end
248
+ end
249
+
250
+ def on_text( body )
251
+ mail.body = NKF.nkf('-j -m0', body)
252
+ mail.set_content_type 'text', 'plain', {'charset' => 'iso-2022-jp'}
253
+ mail.encoding = '7bit'
254
+ end
255
+
256
+ def on_binary( body )
257
+ mail.body = [body].pack('m')
258
+ mail.set_content_type 'application', 'octet-stream'
259
+ mail.encoding = 'Base64'
260
+ end
261
+
262
+ end
263
+
264
+
265
+ class MimeEncodeMulti
266
+
267
+ def exec( mail, top = true )
268
+ mail.mime_version = '1.0' if top
269
+ mail.set_content_type 'multipart', 'mixed'
270
+ e = encoding(nil)
271
+ if e and not /\A(?:7bit|8bit|binary)\z/i === e
272
+ raise ArgumentError,
273
+ 'using C.T.Encoding with multipart mail is not permitted'
274
+ end
275
+ mail.parts.each do |m|
276
+ exec m, false if m._builtin_multipart?
277
+ end
278
+ end
279
+
280
+ end
281
+
282
+ end # module TMail