actionmailer-2.3.17-rack-upgrade 2.3.17

Sign up to get free protection for your applications and to get access to all the features.
Files changed (148) hide show
  1. data/CHANGELOG +387 -0
  2. data/MIT-LICENSE +21 -0
  3. data/README +149 -0
  4. data/Rakefile +97 -0
  5. data/install.rb +30 -0
  6. data/lib/action_mailer.rb +62 -0
  7. data/lib/action_mailer/adv_attr_accessor.rb +30 -0
  8. data/lib/action_mailer/base.rb +739 -0
  9. data/lib/action_mailer/helpers.rb +113 -0
  10. data/lib/action_mailer/mail_helper.rb +17 -0
  11. data/lib/action_mailer/part.rb +107 -0
  12. data/lib/action_mailer/part_container.rb +55 -0
  13. data/lib/action_mailer/quoting.rb +62 -0
  14. data/lib/action_mailer/test_case.rb +64 -0
  15. data/lib/action_mailer/test_helper.rb +68 -0
  16. data/lib/action_mailer/utils.rb +7 -0
  17. data/lib/action_mailer/vendor/text-format-0.6.3/text/format.rb +1466 -0
  18. data/lib/action_mailer/vendor/text_format.rb +10 -0
  19. data/lib/action_mailer/vendor/tmail-1.2.7/tmail.rb +6 -0
  20. data/lib/action_mailer/vendor/tmail-1.2.7/tmail/Makefile +18 -0
  21. data/lib/action_mailer/vendor/tmail-1.2.7/tmail/address.rb +392 -0
  22. data/lib/action_mailer/vendor/tmail-1.2.7/tmail/attachments.rb +65 -0
  23. data/lib/action_mailer/vendor/tmail-1.2.7/tmail/base64.rb +46 -0
  24. data/lib/action_mailer/vendor/tmail-1.2.7/tmail/compat.rb +41 -0
  25. data/lib/action_mailer/vendor/tmail-1.2.7/tmail/config.rb +67 -0
  26. data/lib/action_mailer/vendor/tmail-1.2.7/tmail/core_extensions.rb +63 -0
  27. data/lib/action_mailer/vendor/tmail-1.2.7/tmail/encode.rb +590 -0
  28. data/lib/action_mailer/vendor/tmail-1.2.7/tmail/header.rb +962 -0
  29. data/lib/action_mailer/vendor/tmail-1.2.7/tmail/index.rb +9 -0
  30. data/lib/action_mailer/vendor/tmail-1.2.7/tmail/interface.rb +1162 -0
  31. data/lib/action_mailer/vendor/tmail-1.2.7/tmail/loader.rb +3 -0
  32. data/lib/action_mailer/vendor/tmail-1.2.7/tmail/mail.rb +578 -0
  33. data/lib/action_mailer/vendor/tmail-1.2.7/tmail/mailbox.rb +496 -0
  34. data/lib/action_mailer/vendor/tmail-1.2.7/tmail/main.rb +6 -0
  35. data/lib/action_mailer/vendor/tmail-1.2.7/tmail/mbox.rb +3 -0
  36. data/lib/action_mailer/vendor/tmail-1.2.7/tmail/net.rb +250 -0
  37. data/lib/action_mailer/vendor/tmail-1.2.7/tmail/obsolete.rb +132 -0
  38. data/lib/action_mailer/vendor/tmail-1.2.7/tmail/parser.rb +1060 -0
  39. data/lib/action_mailer/vendor/tmail-1.2.7/tmail/parser.y +416 -0
  40. data/lib/action_mailer/vendor/tmail-1.2.7/tmail/port.rb +379 -0
  41. data/lib/action_mailer/vendor/tmail-1.2.7/tmail/quoting.rb +164 -0
  42. data/lib/action_mailer/vendor/tmail-1.2.7/tmail/require_arch.rb +58 -0
  43. data/lib/action_mailer/vendor/tmail-1.2.7/tmail/scanner.rb +49 -0
  44. data/lib/action_mailer/vendor/tmail-1.2.7/tmail/scanner_r.rb +262 -0
  45. data/lib/action_mailer/vendor/tmail-1.2.7/tmail/stringio.rb +280 -0
  46. data/lib/action_mailer/vendor/tmail-1.2.7/tmail/utils.rb +362 -0
  47. data/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/COPYING +504 -0
  48. data/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/README +12 -0
  49. data/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet.rb +67 -0
  50. data/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/big5freq.rb +927 -0
  51. data/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/big5prober.rb +42 -0
  52. data/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/chardistribution.rb +238 -0
  53. data/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/charsetgroupprober.rb +112 -0
  54. data/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/charsetprober.rb +75 -0
  55. data/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/codingstatemachine.rb +64 -0
  56. data/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/constants.rb +42 -0
  57. data/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/escprober.rb +89 -0
  58. data/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/escsm.rb +244 -0
  59. data/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/eucjpprober.rb +88 -0
  60. data/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/euckrfreq.rb +596 -0
  61. data/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/euckrprober.rb +42 -0
  62. data/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/euctwfreq.rb +430 -0
  63. data/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/euctwprober.rb +42 -0
  64. data/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/gb2312freq.rb +474 -0
  65. data/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/gb2312prober.rb +42 -0
  66. data/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/hebrewprober.rb +289 -0
  67. data/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/jisfreq.rb +570 -0
  68. data/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/jpcntx.rb +229 -0
  69. data/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/langbulgarianmodel.rb +229 -0
  70. data/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/langcyrillicmodel.rb +330 -0
  71. data/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/langgreekmodel.rb +227 -0
  72. data/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/langhebrewmodel.rb +202 -0
  73. data/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/langhungarianmodel.rb +226 -0
  74. data/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/langthaimodel.rb +201 -0
  75. data/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/latin1prober.rb +147 -0
  76. data/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/mbcharsetprober.rb +89 -0
  77. data/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/mbcsgroupprober.rb +45 -0
  78. data/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/mbcssm.rb +542 -0
  79. data/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/sbcharsetprober.rb +124 -0
  80. data/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/sbcsgroupprober.rb +56 -0
  81. data/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/sjisprober.rb +88 -0
  82. data/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/universaldetector.rb +168 -0
  83. data/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/utf8prober.rb +87 -0
  84. data/lib/action_mailer/vendor/tmail-1.2.7/tmail/version.rb +39 -0
  85. data/lib/action_mailer/vendor/tmail.rb +17 -0
  86. data/lib/action_mailer/version.rb +9 -0
  87. data/lib/actionmailer.rb +2 -0
  88. data/test/abstract_unit.rb +62 -0
  89. data/test/asset_host_test.rb +54 -0
  90. data/test/delivery_method_test.rb +51 -0
  91. data/test/fixtures/asset_host_mailer/email_with_asset.html.erb +1 -0
  92. data/test/fixtures/auto_layout_mailer/hello.html.erb +1 -0
  93. data/test/fixtures/auto_layout_mailer/multipart.text.html.erb +1 -0
  94. data/test/fixtures/auto_layout_mailer/multipart.text.plain.erb +1 -0
  95. data/test/fixtures/explicit_layout_mailer/logout.html.erb +1 -0
  96. data/test/fixtures/explicit_layout_mailer/signup.html.erb +1 -0
  97. data/test/fixtures/first_mailer/share.erb +1 -0
  98. data/test/fixtures/helper_mailer/use_example_helper.erb +1 -0
  99. data/test/fixtures/helper_mailer/use_helper.erb +1 -0
  100. data/test/fixtures/helper_mailer/use_helper_method.erb +1 -0
  101. data/test/fixtures/helper_mailer/use_mail_helper.erb +5 -0
  102. data/test/fixtures/helpers/example_helper.rb +5 -0
  103. data/test/fixtures/layouts/auto_layout_mailer.html.erb +1 -0
  104. data/test/fixtures/layouts/auto_layout_mailer.text.erb +1 -0
  105. data/test/fixtures/layouts/spam.html.erb +1 -0
  106. data/test/fixtures/path.with.dots/funky_path_mailer/multipart_with_template_path_with_dots.erb +1 -0
  107. data/test/fixtures/raw_email +14 -0
  108. data/test/fixtures/raw_email10 +20 -0
  109. data/test/fixtures/raw_email12 +32 -0
  110. data/test/fixtures/raw_email13 +29 -0
  111. data/test/fixtures/raw_email2 +114 -0
  112. data/test/fixtures/raw_email3 +70 -0
  113. data/test/fixtures/raw_email4 +59 -0
  114. data/test/fixtures/raw_email5 +19 -0
  115. data/test/fixtures/raw_email6 +20 -0
  116. data/test/fixtures/raw_email7 +66 -0
  117. data/test/fixtures/raw_email8 +47 -0
  118. data/test/fixtures/raw_email9 +28 -0
  119. data/test/fixtures/raw_email_quoted_with_0d0a +14 -0
  120. data/test/fixtures/raw_email_with_invalid_characters_in_content_type +104 -0
  121. data/test/fixtures/raw_email_with_nested_attachment +100 -0
  122. data/test/fixtures/raw_email_with_partially_quoted_subject +14 -0
  123. data/test/fixtures/second_mailer/share.erb +1 -0
  124. data/test/fixtures/templates/signed_up.erb +3 -0
  125. data/test/fixtures/test_mailer/_subtemplate.text.plain.erb +1 -0
  126. data/test/fixtures/test_mailer/body_ivar.erb +2 -0
  127. data/test/fixtures/test_mailer/custom_templating_extension.text.html.haml +6 -0
  128. data/test/fixtures/test_mailer/custom_templating_extension.text.plain.haml +6 -0
  129. data/test/fixtures/test_mailer/implicitly_multipart_example.ignored.erb +1 -0
  130. data/test/fixtures/test_mailer/implicitly_multipart_example.rhtml.bak +1 -0
  131. data/test/fixtures/test_mailer/implicitly_multipart_example.text.html.erb +10 -0
  132. data/test/fixtures/test_mailer/implicitly_multipart_example.text.html.erb~ +10 -0
  133. data/test/fixtures/test_mailer/implicitly_multipart_example.text.plain.erb +2 -0
  134. data/test/fixtures/test_mailer/implicitly_multipart_example.text.yaml.erb +1 -0
  135. data/test/fixtures/test_mailer/included_subtemplate.text.plain.erb +1 -0
  136. data/test/fixtures/test_mailer/rxml_template.builder +2 -0
  137. data/test/fixtures/test_mailer/rxml_template.rxml +2 -0
  138. data/test/fixtures/test_mailer/signed_up.html.erb +3 -0
  139. data/test/fixtures/test_mailer/signed_up_with_url.erb +5 -0
  140. data/test/mail_helper_test.rb +95 -0
  141. data/test/mail_layout_test.rb +123 -0
  142. data/test/mail_render_test.rb +116 -0
  143. data/test/mail_service_test.rb +1145 -0
  144. data/test/quoting_test.rb +105 -0
  145. data/test/test_helper_test.rb +129 -0
  146. data/test/tmail_test.rb +22 -0
  147. data/test/url_test.rb +76 -0
  148. metadata +209 -0
@@ -0,0 +1,496 @@
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
+ class << self
154
+ alias newobj new
155
+ end
156
+
157
+ # Creates a new mailbox object that you can iterate through to collect the
158
+ # emails from with "each_port".
159
+ #
160
+ # You need to pass it a filename of a unix mailbox format file, the format of this
161
+ # file can be researched at this page at {wikipedia}[link:http://en.wikipedia.org/wiki/Mbox]
162
+ #
163
+ # ==== Parameters
164
+ #
165
+ # +filename+: The filename of the mailbox you want to open
166
+ #
167
+ # +tmpdir+: Can be set to override TMail using the system environment's temp dir. TMail will first
168
+ # use the temp dir specified by you (if any) or then the temp dir specified in the Environment's TEMP
169
+ # value then the value in the Environment's TMP value or failing all of the above, '/tmp'
170
+ #
171
+ # +readonly+: If set to false, each email you take from the mail box will be removed from the mailbox.
172
+ # default is *false* - ie, it *WILL* truncate your mailbox file to ZERO once it has read the emails out.
173
+ #
174
+ # ==== Options:
175
+ #
176
+ # None
177
+ #
178
+ # ==== Examples:
179
+ #
180
+ # # First show using readonly true:
181
+ #
182
+ # require 'ftools'
183
+ # File.size("../test/fixtures/mailbox")
184
+ # #=> 20426
185
+ #
186
+ # mailbox = TMail::UNIXMbox.new("../test/fixtures/mailbox", nil, true)
187
+ # #=> #<TMail::UNIXMbox:0x14a2aa8 @readonly=true.....>
188
+ #
189
+ # mailbox.each_port do |port|
190
+ # mail = TMail::Mail.new(port)
191
+ # puts mail.subject
192
+ # end
193
+ # #Testing mailbox 1
194
+ # #Testing mailbox 2
195
+ # #Testing mailbox 3
196
+ # #Testing mailbox 4
197
+ # require 'ftools'
198
+ # File.size?("../test/fixtures/mailbox")
199
+ # #=> 20426
200
+ #
201
+ # # Now show with readonly set to the default false
202
+ #
203
+ # mailbox = TMail::UNIXMbox.new("../test/fixtures/mailbox")
204
+ # #=> #<TMail::UNIXMbox:0x14a2aa8 @readonly=false.....>
205
+ #
206
+ # mailbox.each_port do |port|
207
+ # mail = TMail::Mail.new(port)
208
+ # puts mail.subject
209
+ # end
210
+ # #Testing mailbox 1
211
+ # #Testing mailbox 2
212
+ # #Testing mailbox 3
213
+ # #Testing mailbox 4
214
+ #
215
+ # File.size?("../test/fixtures/mailbox")
216
+ # #=> nil
217
+ def UNIXMbox.new( filename, tmpdir = nil, readonly = false )
218
+ tmpdir = ENV['TEMP'] || ENV['TMP'] || '/tmp'
219
+ newobj(filename, "#{tmpdir}/ruby_tmail_#{$$}_#{rand()}", readonly, false)
220
+ end
221
+
222
+ def UNIXMbox.lock( fname )
223
+ begin
224
+ f = File.open(fname, 'r+')
225
+ f.flock File::LOCK_EX
226
+ yield f
227
+ ensure
228
+ f.flock File::LOCK_UN
229
+ f.close if f and not f.closed?
230
+ end
231
+ end
232
+
233
+ def UNIXMbox.static_new( fname, dir, readonly = false )
234
+ newobj(fname, dir, readonly, true)
235
+ end
236
+
237
+ def initialize( fname, mhdir, readonly, static )
238
+ @filename = fname
239
+ @readonly = readonly
240
+ @closed = false
241
+
242
+ Dir.mkdir mhdir
243
+ @real = MhMailbox.new(mhdir)
244
+ @finalizer = UNIXMbox.mkfinal(@real, @filename, !@readonly, !static)
245
+ ObjectSpace.define_finalizer self, @finalizer
246
+ end
247
+
248
+ def UNIXMbox.mkfinal( mh, mboxfile, writeback_p, cleanup_p )
249
+ lambda {
250
+ if writeback_p
251
+ lock(mboxfile) {|f|
252
+ mh.each_port do |port|
253
+ f.puts create_from_line(port)
254
+ port.ropen {|r|
255
+ f.puts r.read
256
+ }
257
+ end
258
+ }
259
+ end
260
+ if cleanup_p
261
+ Dir.foreach(mh.dirname) do |fname|
262
+ next if /\A\.\.?\z/ === fname
263
+ File.unlink "#{mh.dirname}/#{fname}"
264
+ end
265
+ Dir.rmdir mh.dirname
266
+ end
267
+ }
268
+ end
269
+
270
+ # make _From line
271
+ def UNIXMbox.create_from_line( port )
272
+ sprintf 'From %s %s',
273
+ fromaddr(), TextUtils.time2str(File.mtime(port.filename))
274
+ end
275
+
276
+ def UNIXMbox.fromaddr(port)
277
+ h = HeaderField.new_from_port(port, 'Return-Path') ||
278
+ HeaderField.new_from_port(port, 'From') ||
279
+ HeaderField.new_from_port(port, 'EnvelopeSender') or return 'nobody'
280
+ a = h.addrs[0] or return 'nobody'
281
+ a.spec
282
+ end
283
+
284
+ def close
285
+ return if @closed
286
+
287
+ ObjectSpace.undefine_finalizer self
288
+ @finalizer.call
289
+ @finalizer = nil
290
+ @real = nil
291
+ @closed = true
292
+ @updated = nil
293
+ end
294
+
295
+ def each_port( &block )
296
+ close_check
297
+ update
298
+ @real.each_port(&block)
299
+ end
300
+
301
+ alias each each_port
302
+
303
+ def reverse_each_port( &block )
304
+ close_check
305
+ update
306
+ @real.reverse_each_port(&block)
307
+ end
308
+
309
+ alias reverse_each reverse_each_port
310
+
311
+ # old #each_mail returns Port
312
+ #def each_mail( &block )
313
+ # each_port do |port|
314
+ # yield Mail.new(port)
315
+ # end
316
+ #end
317
+
318
+ def each_new_port( mtime = nil )
319
+ close_check
320
+ update
321
+ @real.each_new_port(mtime) {|p| yield p }
322
+ end
323
+
324
+ def new_port
325
+ close_check
326
+ @real.new_port
327
+ end
328
+
329
+ private
330
+
331
+ def close_check
332
+ @closed and raise ArgumentError, 'accessing already closed mbox'
333
+ end
334
+
335
+ def update
336
+ return if FileTest.zero?(@filename)
337
+ return if @updated and File.mtime(@filename) < @updated
338
+ w = nil
339
+ port = nil
340
+ time = nil
341
+ UNIXMbox.lock(@filename) {|f|
342
+ begin
343
+ f.each do |line|
344
+ if /\AFrom / === line
345
+ w.close if w
346
+ File.utime time, time, port.filename if time
347
+
348
+ port = @real.new_port
349
+ w = port.wopen
350
+ time = fromline2time(line)
351
+ else
352
+ w.print line if w
353
+ end
354
+ end
355
+ ensure
356
+ if w and not w.closed?
357
+ w.close
358
+ File.utime time, time, port.filename if time
359
+ end
360
+ end
361
+ f.truncate(0) unless @readonly
362
+ @updated = Time.now
363
+ }
364
+ end
365
+
366
+ def fromline2time( line )
367
+ m = /\AFrom \S+ \w+ (\w+) (\d+) (\d+):(\d+):(\d+) (\d+)/.match(line) or return nil
368
+ Time.local(m[6].to_i, m[1], m[2].to_i, m[3].to_i, m[4].to_i, m[5].to_i)
369
+ rescue
370
+ nil
371
+ end
372
+
373
+ end # UNIXMbox
374
+
375
+ MboxLoader = UNIXMbox
376
+
377
+
378
+ class Maildir
379
+
380
+ extend Mutex_m
381
+
382
+ PORT_CLASS = MaildirPort
383
+
384
+ @seq = 0
385
+ def Maildir.unique_number
386
+ synchronize {
387
+ @seq += 1
388
+ return @seq
389
+ }
390
+ end
391
+
392
+ def initialize( dir = nil )
393
+ @dirname = dir || ENV['MAILDIR']
394
+ raise ArgumentError, "not directory: #{@dirname}"\
395
+ unless FileTest.directory? @dirname
396
+ @new = "#{@dirname}/new"
397
+ @tmp = "#{@dirname}/tmp"
398
+ @cur = "#{@dirname}/cur"
399
+ end
400
+
401
+ def directory
402
+ @dirname
403
+ end
404
+
405
+ def inspect
406
+ "#<#{self.class} #{@dirname}>"
407
+ end
408
+
409
+ def close
410
+ end
411
+
412
+ def each_port
413
+ mail_files(@cur).each do |path|
414
+ yield PORT_CLASS.new(path)
415
+ end
416
+ end
417
+
418
+ alias each each_port
419
+
420
+ def reverse_each_port
421
+ mail_files(@cur).reverse_each do |path|
422
+ yield PORT_CLASS.new(path)
423
+ end
424
+ end
425
+
426
+ alias reverse_each reverse_each_port
427
+
428
+ def new_port
429
+ fname = nil
430
+ tmpfname = nil
431
+ newfname = nil
432
+
433
+ begin
434
+ fname = "#{Time.now.to_i}.#{$$}_#{Maildir.unique_number}.#{Socket.gethostname}"
435
+
436
+ tmpfname = "#{@tmp}/#{fname}"
437
+ newfname = "#{@new}/#{fname}"
438
+ end while FileTest.exist? tmpfname
439
+
440
+ if block_given?
441
+ File.open(tmpfname, 'w') {|f| yield f }
442
+ File.rename tmpfname, newfname
443
+ PORT_CLASS.new(newfname)
444
+ else
445
+ File.open(tmpfname, 'w') {|f| f.write "\n\n" }
446
+ PORT_CLASS.new(tmpfname)
447
+ end
448
+ end
449
+
450
+ def each_new_port
451
+ mail_files(@new).each do |path|
452
+ dest = @cur + '/' + File.basename(path)
453
+ File.rename path, dest
454
+ yield PORT_CLASS.new(dest)
455
+ end
456
+
457
+ check_tmp
458
+ end
459
+
460
+ TOO_OLD = 60 * 60 * 36 # 36 hour
461
+
462
+ def check_tmp
463
+ old = Time.now.to_i - TOO_OLD
464
+
465
+ each_filename(@tmp) do |full, fname|
466
+ if FileTest.file? full and
467
+ File.stat(full).mtime.to_i < old
468
+ File.unlink full
469
+ end
470
+ end
471
+ end
472
+
473
+ private
474
+
475
+ def mail_files( dir )
476
+ Dir.entries(dir)\
477
+ .select {|s| s[0] != ?. }\
478
+ .sort_by {|s| s.slice(/\A\d+/).to_i }\
479
+ .map {|s| "#{dir}/#{s}" }\
480
+ .select {|path| FileTest.file? path }
481
+ end
482
+
483
+ def each_filename( dir )
484
+ Dir.foreach(dir) do |fname|
485
+ path = "#{dir}/#{fname}"
486
+ if fname[0] != ?. and FileTest.file? path
487
+ yield path, fname
488
+ end
489
+ end
490
+ end
491
+
492
+ end # Maildir
493
+
494
+ MaildirLoader = Maildir
495
+
496
+ end # module TMail