Froze rails gems
[depot.git] / vendor / rails / actionmailer / lib / action_mailer / vendor / tmail-1.2.3 / tmail / mailbox.rb
diff --git a/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.3/tmail/mailbox.rb b/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.3/tmail/mailbox.rb
new file mode 100644 (file)
index 0000000..b0bc6a7
--- /dev/null
@@ -0,0 +1,495 @@
+=begin rdoc
+
+= Mailbox and Mbox interaction class
+
+=end
+#--
+# Copyright (c) 1998-2003 Minero Aoki <aamine@loveruby.net>
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+# Note: Originally licensed under LGPL v2+. Using MIT license for Rails
+# with permission of Minero Aoki.
+#++
+
+require 'tmail/port'
+require 'socket'
+require 'mutex_m'
+
+
+unless [].respond_to?(:sort_by)
+module Enumerable#:nodoc:
+  def sort_by
+    map {|i| [yield(i), i] }.sort {|a,b| a.first <=> b.first }.map {|i| i[1] }
+  end
+end
+end
+
+
+module TMail
+
+  class MhMailbox
+
+    PORT_CLASS = MhPort
+
+    def initialize( dir )
+      edir = File.expand_path(dir)
+      raise ArgumentError, "not directory: #{dir}"\
+                              unless FileTest.directory? edir
+      @dirname = edir
+      @last_file = nil
+      @last_atime = nil
+    end
+
+    def directory
+      @dirname
+    end
+
+    alias dirname directory
+
+    attr_accessor :last_atime
+
+    def inspect
+      "#<#{self.class} #{@dirname}>"
+    end
+
+    def close
+    end
+
+    def new_port
+      PORT_CLASS.new(next_file_name())
+    end
+
+    def each_port
+      mail_files().each do |path|
+        yield PORT_CLASS.new(path)
+      end
+      @last_atime = Time.now
+    end
+
+    alias each each_port
+
+    def reverse_each_port
+      mail_files().reverse_each do |path|
+        yield PORT_CLASS.new(path)
+      end
+      @last_atime = Time.now
+    end
+
+    alias reverse_each reverse_each_port
+
+    # old #each_mail returns Port
+    #def each_mail
+    #  each_port do |port|
+    #    yield Mail.new(port)
+    #  end
+    #end
+
+    def each_new_port( mtime = nil, &block )
+      mtime ||= @last_atime
+      return each_port(&block) unless mtime
+      return unless File.mtime(@dirname) >= mtime
+
+      mail_files().each do |path|
+        yield PORT_CLASS.new(path) if File.mtime(path) > mtime
+      end
+      @last_atime = Time.now
+    end
+
+    private
+
+    def mail_files
+      Dir.entries(@dirname)\
+              .select {|s| /\A\d+\z/ === s }\
+              .map {|s| s.to_i }\
+              .sort\
+              .map {|i| "#{@dirname}/#{i}" }\
+              .select {|path| FileTest.file? path }
+    end
+
+    def next_file_name
+      unless n = @last_file
+        n = 0
+        Dir.entries(@dirname)\
+                .select {|s| /\A\d+\z/ === s }\
+                .map {|s| s.to_i }.sort\
+        .each do |i|
+          next unless FileTest.file? "#{@dirname}/#{i}"
+          n = i
+        end
+      end
+      begin
+        n += 1
+      end while FileTest.exist? "#{@dirname}/#{n}"
+      @last_file = n
+
+      "#{@dirname}/#{n}"
+    end
+
+  end   # MhMailbox
+
+  MhLoader = MhMailbox
+
+
+  class UNIXMbox
+  
+    class << self
+      alias newobj new
+    end
+
+    # Creates a new mailbox object that you can iterate through to collect the
+    # emails from with "each_port".  
+    # 
+    # You need to pass it a filename of a unix mailbox format file, the format of this
+    # file can be researched at this page at {wikipedia}[link:http://en.wikipedia.org/wiki/Mbox]
+    # 
+    # ==== Parameters
+    # 
+    # +filename+: The filename of the mailbox you want to open
+    # 
+    # +tmpdir+: Can be set to override TMail using the system environment's temp dir. TMail will first
+    # use the temp dir specified by you (if any) or then the temp dir specified in the Environment's TEMP
+    # value then the value in the Environment's TMP value or failing all of the above, '/tmp'
+    # 
+    # +readonly+: If set to false, each email you take from the mail box will be removed from the mailbox.
+    # default is *false* - ie, it *WILL* truncate your mailbox file to ZERO once it has read the emails out.
+    # 
+    # ==== Options:
+    # 
+    # None
+    # 
+    # ==== Examples:
+    # 
+    #  # First show using readonly true:
+    # 
+    #  require 'ftools'
+    #  File.size("../test/fixtures/mailbox")
+    #  #=> 20426
+    #
+    #  mailbox = TMail::UNIXMbox.new("../test/fixtures/mailbox", nil, true) 
+    #  #=> #<TMail::UNIXMbox:0x14a2aa8 @readonly=true.....>
+    #
+    #  mailbox.each_port do |port| 
+    #    mail = TMail::Mail.new(port)
+    #    puts mail.subject
+    #  end
+    #  #Testing mailbox 1
+    #  #Testing mailbox 2
+    #  #Testing mailbox 3
+    #  #Testing mailbox 4
+    #  require 'ftools'
+    #  File.size?("../test/fixtures/mailbox")
+    #  #=> 20426
+    # 
+    #  # Now show with readonly set to the default false
+    # 
+    #  mailbox = TMail::UNIXMbox.new("../test/fixtures/mailbox") 
+    #  #=> #<TMail::UNIXMbox:0x14a2aa8 @readonly=false.....>
+    #
+    #  mailbox.each_port do |port| 
+    #    mail = TMail::Mail.new(port)
+    #    puts mail.subject
+    #  end
+    #  #Testing mailbox 1
+    #  #Testing mailbox 2
+    #  #Testing mailbox 3
+    #  #Testing mailbox 4
+    # 
+    #  File.size?("../test/fixtures/mailbox")
+    #  #=> nil
+    def UNIXMbox.new( filename, tmpdir = nil, readonly = false )
+      tmpdir = ENV['TEMP'] || ENV['TMP'] || '/tmp'
+      newobj(filename, "#{tmpdir}/ruby_tmail_#{$$}_#{rand()}", readonly, false)
+    end
+
+    def UNIXMbox.lock( fname )
+      begin
+        f = File.open(fname, 'r+')
+        f.flock File::LOCK_EX
+        yield f
+      ensure
+        f.flock File::LOCK_UN
+        f.close if f and not f.closed?
+      end
+    end
+
+    def UNIXMbox.static_new( fname, dir, readonly = false )
+      newobj(fname, dir, readonly, true)
+    end
+
+    def initialize( fname, mhdir, readonly, static )
+      @filename = fname
+      @readonly = readonly
+      @closed = false
+
+      Dir.mkdir mhdir
+      @real = MhMailbox.new(mhdir)
+      @finalizer = UNIXMbox.mkfinal(@real, @filename, !@readonly, !static)
+      ObjectSpace.define_finalizer self, @finalizer
+    end
+
+    def UNIXMbox.mkfinal( mh, mboxfile, writeback_p, cleanup_p )
+      lambda {
+          if writeback_p
+            lock(mboxfile) {|f|
+                mh.each_port do |port|
+                  f.puts create_from_line(port)
+                  port.ropen {|r|
+                      f.puts r.read
+                  }
+                end
+            }
+          end
+          if cleanup_p
+            Dir.foreach(mh.dirname) do |fname|
+              next if /\A\.\.?\z/ === fname
+              File.unlink "#{mh.dirname}/#{fname}"
+            end
+            Dir.rmdir mh.dirname
+          end
+      }
+    end
+
+    # make _From line
+    def UNIXMbox.create_from_line( port )
+      sprintf 'From %s %s',
+              fromaddr(), TextUtils.time2str(File.mtime(port.filename))
+    end
+
+    def UNIXMbox.fromaddr(port)
+      h = HeaderField.new_from_port(port, 'Return-Path') ||
+      HeaderField.new_from_port(port, 'From') ||
+      HeaderField.new_from_port(port, 'EnvelopeSender') or return 'nobody'
+      a = h.addrs[0] or return 'nobody'
+      a.spec
+    end
+
+    def close
+      return if @closed
+
+      ObjectSpace.undefine_finalizer self
+      @finalizer.call
+      @finalizer = nil
+      @real = nil
+      @closed = true
+      @updated = nil
+    end
+
+    def each_port( &block )
+      close_check
+      update
+      @real.each_port(&block)
+    end
+
+    alias each each_port
+
+    def reverse_each_port( &block )
+      close_check
+      update
+      @real.reverse_each_port(&block)
+    end
+
+    alias reverse_each reverse_each_port
+
+    # old #each_mail returns Port
+    #def each_mail( &block )
+    #  each_port do |port|
+    #    yield Mail.new(port)
+    #  end
+    #end
+
+    def each_new_port( mtime = nil )
+      close_check
+      update
+      @real.each_new_port(mtime) {|p| yield p }
+    end
+
+    def new_port
+      close_check
+      @real.new_port
+    end
+
+    private
+
+    def close_check
+      @closed and raise ArgumentError, 'accessing already closed mbox'
+    end
+
+    def update
+      return if FileTest.zero?(@filename)
+      return if @updated and File.mtime(@filename) < @updated
+      w = nil
+      port = nil
+      time = nil
+      UNIXMbox.lock(@filename) {|f|
+          begin
+            f.each do |line|
+              if /\AFrom / === line
+                w.close if w
+                File.utime time, time, port.filename if time
+
+                port = @real.new_port
+                w = port.wopen
+                time = fromline2time(line)
+              else
+                w.print line if w
+              end
+            end
+          ensure
+            if w and not w.closed?
+              w.close
+              File.utime time, time, port.filename if time
+            end
+          end
+          f.truncate(0) unless @readonly
+          @updated = Time.now
+      }
+    end
+
+    def fromline2time( line )
+      m = /\AFrom \S+ \w+ (\w+) (\d+) (\d+):(\d+):(\d+) (\d+)/.match(line) \
+              or return nil
+      Time.local(m[6].to_i, m[1], m[2].to_i, m[3].to_i, m[4].to_i, m[5].to_i)
+    end
+
+  end   # UNIXMbox
+
+  MboxLoader = UNIXMbox
+
+
+  class Maildir
+
+    extend Mutex_m
+
+    PORT_CLASS = MaildirPort
+
+    @seq = 0
+    def Maildir.unique_number
+      synchronize {
+          @seq += 1
+          return @seq
+      }
+    end
+
+    def initialize( dir = nil )
+      @dirname = dir || ENV['MAILDIR']
+      raise ArgumentError, "not directory: #{@dirname}"\
+                              unless FileTest.directory? @dirname
+      @new = "#{@dirname}/new"
+      @tmp = "#{@dirname}/tmp"
+      @cur = "#{@dirname}/cur"
+    end
+
+    def directory
+      @dirname
+    end
+
+    def inspect
+      "#<#{self.class} #{@dirname}>"
+    end
+
+    def close
+    end
+
+    def each_port
+      mail_files(@cur).each do |path|
+        yield PORT_CLASS.new(path)
+      end
+    end
+
+    alias each each_port
+
+    def reverse_each_port
+      mail_files(@cur).reverse_each do |path|
+        yield PORT_CLASS.new(path)
+      end
+    end
+
+    alias reverse_each reverse_each_port
+
+    def new_port
+      fname = nil
+      tmpfname = nil
+      newfname = nil
+
+      begin
+        fname = "#{Time.now.to_i}.#{$$}_#{Maildir.unique_number}.#{Socket.gethostname}"
+        
+        tmpfname = "#{@tmp}/#{fname}"
+        newfname = "#{@new}/#{fname}"
+      end while FileTest.exist? tmpfname
+
+      if block_given?
+        File.open(tmpfname, 'w') {|f| yield f }
+        File.rename tmpfname, newfname
+        PORT_CLASS.new(newfname)
+      else
+        File.open(tmpfname, 'w') {|f| f.write "\n\n" }
+        PORT_CLASS.new(tmpfname)
+      end
+    end
+
+    def each_new_port
+      mail_files(@new).each do |path|
+        dest = @cur + '/' + File.basename(path)
+        File.rename path, dest
+        yield PORT_CLASS.new(dest)
+      end
+
+      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 FileTest.file? full and
+           File.stat(full).mtime.to_i < old
+          File.unlink full
+        end
+      end
+    end
+
+    private
+
+    def mail_files( dir )
+      Dir.entries(dir)\
+              .select {|s| s[0] != ?. }\
+              .sort_by {|s| s.slice(/\A\d+/).to_i }\
+              .map {|s| "#{dir}/#{s}" }\
+              .select {|path| FileTest.file? path }
+    end
+
+    def each_filename( dir )
+      Dir.foreach(dir) do |fname|
+        path = "#{dir}/#{fname}"
+        if fname[0] != ?. and FileTest.file? path
+          yield path, fname
+        end
+      end
+    end
+    
+  end   # Maildir
+
+  MaildirLoader = Maildir
+
+end   # module TMail