7 # Copyright (c) 1998-2003 Minero Aoki <aamine@loveruby.net>
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:
17 # The above copyright notice and this permission notice shall be
18 # included in all copies or substantial portions of the Software.
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.
28 # Note: Originally licensed under LGPL v2+. Using MIT license for Rails
29 # with permission of Minero Aoki.
32 require 'tmail/stringio'
50 def initialize( fname
)
51 @filename = File
.expand_path(fname
)
60 other
.respond_to
?(:filename) and @filename == other
.filename
70 "#<#{self.class}:#{@filename}>"
83 File
.open(@filename, &block
)
87 File
.open(@filename, 'w', &block
)
91 File
.open(@filename, 'a', &block
)
103 File
.unlink
@filename
108 File
.link
@filename, port
.filename
112 File
.unlink
@filename
119 copy_file
@filename, port
.filename
121 File
.open(@filename) {|r
|
123 while s
= r
.sysread(4096)
135 def copy_file( src
, dest
)
138 File
.open(src
, 'rb') {|r
|
139 File
.open(dest
, 'wb') {|w
|
143 w
.write r
.sysread(st
.blksize
)
181 def procinfostr( str
, tag
, true_p
)
182 a
= str
.upcase
.split(//)
183 a
.push true_p
? tag
: nil
184 a
.delete tag
unless true_p
185 a
.compact
.sort
.join('').squeeze
191 class MhPort
< FilePort
197 def set_status( tag
, flag
)
199 tmpfile
= @filename + '.tmailtmp.' + $$
.to_s
200 File
.open(tmpfile
, 'w') {|f
|
201 write_status f
, tag
, flag
203 File
.unlink
@filename
204 File
.link tmpfile
, @filename
210 def write_status( f
, tag
, flag
)
212 File
.open(@filename) {|r
|
216 elsif m
= /\AX-TMail-Status:/i
.match(line
)
217 stat
= m
.post_match
.strip
223 s
= procinfostr(stat
, tag
, flag
)
224 f
.puts
'X-TMail-Status: ' + s
unless s
.empty
?
227 while s
= r
.read(2048)
233 def get_status( tag
)
234 File
.foreach(@filename) {|line
|
235 return false if line
.strip
.empty
?
236 if m
= /\AX-TMail-Status:/i
.match(line
)
237 return m
.post_match
.strip
.include?(tag
[0])
246 class MaildirPort
< FilePort
249 new
= replace_dir(@filename, 'new')
250 File
.rename
@filename, new
255 new
= replace_dir(@filename, 'cur')
256 File
.rename
@filename, new
260 def replace_dir( path
, dir
)
261 "#{File.dirname File.dirname(path)}/#{dir}/#{File.basename path}"
270 MAIL_FILE
= /\A(\d+\.[\d_]+\.[^:]+)(?:\:(\d),(\w+)?)?\z/
272 def set_status( tag
, flag
)
273 if m
= MAIL_FILE
.match(File
.basename(@filename))
274 s
, uniq
, type
, info
, = m
.to_a
275 return if type
and type
!= '2' # do not change anything
276 newname
= File
.dirname(@filename) + '/' +
277 uniq
+ ':2,' + procinfostr(info
.to_s
, tag
, flag
)
279 newname
= @filename + ':2,' + tag
282 File
.link
@filename, newname
283 File
.unlink
@filename
287 def get_status( tag
)
288 m
= MAIL_FILE
.match(File
.basename(@filename)) or return false
289 m
[2] == '2' and m
[3].to_s
.include?(tag
[0])
299 class StringPort
< Port
301 def initialize( str
= '' )
321 StringPort
=== other
and @buffer.equal
? other
.string
327 @buffer.object_id
.hash
331 "#<#{self.class}:id=#{sprintf '0x%x', @buffer.object_id}>"
339 @buffer or raise Errno
::ENOENT, "#{inspect} is already removed"
340 StringInput
.open(@buffer, &block
)
345 StringOutput
.new(@buffer, &block
)
350 StringOutput
.new(@buffer, &block
)
368 if StringPort
=== port
370 port
.instance_eval
{ @buffer = str
}