tmail/tmail/loader.rb
#
# loader.rb
#
# Copyright (c) 1999 Minero Aoki <aamine@dp.u-netsurf.ne.jp>
#
# This program is free software.
# You can distribute/modify this program under the terms of
# the GNU Lesser General Public License version 2 or later.
#
require 'amstd/futils'
module TMail
class Loader
include FileUtils
def each_filename
filenames.each {|i| yield i }
end
end
class MhLoader < Loader
def initialize( dname )
@dirname = mustdir( dname )
@oldnext = nil
@lasttime = nil
end
def filenames
arr = []
foreach_fullpath( @dirname ) do |path|
next unless /\A\d+\z/o === basename(path)
next unless file? path
arr.push path
end
arr
end
def nextfile
n = @oldnext
unless n then
n = 0
each_filename do |fname, base|
i = base.to_i
n = i if i > n
end
n += 1
end
while exist? sepjoin( @dirname, n ) do
n += 1
end
@oldnext = n
return sepjoin( @dirname, n )
end
def each_mail
arr = []
@lasttime = Time.now
each_filename do |fname|
yield FilePort.new( fname )
end
end
alias each each_mail
def new_mail
return FilePort.new( nextfile )
end
alias new_port new_mail
def each_newmail( mtime = nil )
mtime ||= @lasttime
raise ArgumentError, "last update time is not given"
return if mtime > File.mtime( @dirname )
@lasttime = Time.now
each_filename do |fname|
if File.mtime( fname ) > mtime then
yield FilePort.new( fname )
end
end
end
end
class MboxLoader < Loader
def initialize( fname, tempdir = nil )
@filename = expand( fname )
unless file? @filename then
raise ArgumentError, "'#{fname}' is not normal file"
end
if tempdir then
@tempdir = mustdir( tempdir )
else
@tempdir = isdir( "/tmp/tmail_mboxloader_#{$$}_#{id}" )
@finalizer = MboxLoader.mkproc( @tempdir )
ObjectSpace.add_finalizer @finalizer
end
@real = nil
@closed = false
end
def close
return if @closed
@closed = true
@real = nil
@tempdir = nil
@finalizer.call
@finalizer = nil
end
def each_mail
close_check
sync
@real.each_mail {|p| yield p }
end
alias each each_mail
def each_newmail( mtime = nil )
close_check
sync
@real.each_newmail( mtime ) {|p| yield p }
end
def new_mail
raise
end
alias new_port new_mail
private
def sync
wf = nil
n = 1
lock do |f|
begin
f.each do |line|
if /\AFrom /o then
if wf then
wf.close
end
wf = File.open( sepjoin(@tempdir, n) )
n += 1
else
wf << line if wf
end
end
if port then
ws.close
yield port
end
ensure
if wf and not wf.closed? then
wf.close
end
end
end
@real = MhLoader.new( @tempdir )
end
def lock
begin
f = File.open( @filename )
begin
f.flock File::LOCK_EX
yield f
ensure
f.flock File::LOCK_UN
end
ensure
f.close if f
end
end
end
class MaildirLoader < Loader
def initialize( dname )
@dirname = mustdir( dname )
@new = mustdir( sepjoin( dname, 'new' ) )
@tmp = mustdir( sepjoin( dname, 'tmp' ) )
@cur = mustdir( sepjoin( dname, 'cur' ) )
end
def close
@closed = true
end
def each_filename( dn )
Dir.foreach( dn ) do |fn|
full = sepjoin(dn, fn)
if fn[0] != ?. and File.file? full then
yield full, fn
end
end
end
def each_mail
each_filename( @tmp ) do |full, fname|
yield FilePort.new( full )
end
end
alias each each_mail
def new_mail
fn = nil
tmp = nil
i = 0
while true do
fn = "#{Time.now.to_i}.#{$$}.#{ENV['HOSTNAME']}"
tmp = sepjoin(@tmp, fn )
break unless File.exist? tmp
i += 1
if i == 3 then
raise IOError, "can't create new file in maildir"
end
sleep 1 #2
end
File.open( tmp, 'w' ){|f| f.write "\n\n" }
cur = sepjoin(@cur, fn)
File.rename tmp, cur
FilePort.new( cur )
end
alias new_port new_mail
def each_newmail
arr = []
each_filename( @new ) do |old, fname|
new = sepjoin(@new, fname + ':2,')
File.rename old, new
arr.push FilePort.new( new )
end
arr.each{|i| yield i }
check_tmp
end
TOO_OLD = 60 * 60 * 36 # 36 hour
def check_tmp
old = Time.now.to_i - TOO_OLD
each_filename( @tmp ) do |full, fname|
if File.file? full then
if File.stat(full).mtime.to_i < nt then
File.rm_f full
end
end
end
end
end
end # module TMail