rubyfilter-0.12.orig/0000755001013300101330000000000007773224251016167 5ustar yaegashiyaegashi00000000000000rubyfilter-0.12.orig/bin/0000755001013300101330000000000007773224251016737 5ustar yaegashiyaegashi00000000000000rubyfilter-0.12.orig/bin/rsendmail.rb0000444001013300101330000001076407773224251021250 0ustar yaegashiyaegashi00000000000000#!/usr/bin/env ruby #-- # Copyright (C) 2002 Matt Armstrong. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # 3. The name of the author may not be used to endorse or promote # products derived from this software without specific prior # written permission. # # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE # GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER # IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR # OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN # IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # require 'rmail/parser' require 'rmail/address' require 'rmail/serialize' def syslog(str) system('logger', '-p', 'mail.info', '-t', 'rsendmail', str.to_s) end extract_recipients = false sender = nil full_name = nil single_dot_ends = true end_of_options = false recipients = [] while not ARGV.empty? arg = ARGV.shift if ! recipients.empty? || arg =~ /\A[^-]/ || end_of_options recipients.push(arg) else case arg when '-t' extract_recipients = true when '-f' sender = ARGV.shift when '-F' full_name = ARGV.shift when '-oi' single_dot_ends = false when '--' end_of_options = true else $stderr.puts("rsendmail: invalid option -- #{arg}") exit 1 end end end # FIXME: parsing messages should be external to the RMail::Message # class. For example, we should differentiate here between -oi and # not -oi. message = RMail::Parser.new.parse($stdin) def get_recipients(message, field_name, list) unless message.header[field_name].nil? RMail::Address.parse(message.header[field_name]).each do |address| # FIXME: need an "smtpaddress" method list.push(address.address) end end end if extract_recipients get_recipients(message, 'to', recipients) get_recipients(message, 'cc', recipients) get_recipients(message, 'bcc', recipients) end # FIXME: put this into some kind of library # FIXME: simplify verp recipients? # FIXME: share with .rdeliver def with_db(name) require 'gdbm' begin db = nil begin db = GDBM::open(File.join("/home/matt/.rfilter/var", name), 0600) rescue Errno::EWOULDBLOCK # FIXME: only wait so long, then defer sleep(2) retry end yield db ensure db.close unless db.nil? end end # FIXME: share with .rdeliver def record_string_in_db(db, address) record = db[address] count = record.split(/:/)[1] unless record.nil? count ||= '0' db[address] = Time.now.strftime("%Y%m%d") + ':' + count.succ end with_db('sent-recipient') do |db| dup = {} recipients.each do |r| address = r.downcase record_string_in_db(db, address) unless dup.key?(address) dup[address] ||= 1 end end with_db "sent-subjects" do |db| if subject = message.header['subject'] subject = subject.strip.downcase record_string_in_db(db, subject) end end with_db "sent-msgid" do |db| if msgid = message.header['message-id'] msgid = msgid.strip.downcase record_string_in_db(db, msgid) end end # FIXME: delete any bcc headers # FIXME: should be able to generate a default From: header #raise 'no from header' if message.header['from'].nil? # FIXME: more error checking here IO.popen('-', 'w') do |child| if child.nil? # FIXME: instead of 'address' need a way to output the address for # SMTP purposes. command = ['/usr/sbin/sendmail', '-oi'] command.concat(['-F', full_name]) if full_name command.concat(['-f', sender]) if sender command.concat(recipients) #syslog("args ouggoing: " + command.inspect) exec(*command) else RMail::Serialize.new(child).serialize(message) end end rubyfilter-0.12.orig/bin/rdeliver.rb0000444001013300101330000002131407773224251021077 0ustar yaegashiyaegashi00000000000000#!/usr/bin/env ruby #-- # Copyright (C) 2001, 2002, 2003 Matt Armstrong. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # 3. The name of the author may not be used to endorse or promote # products derived from this software without specific prior # written permission. # # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE # GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER # IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR # OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN # IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # #++ # This script serves as an example of how you can use the # RFilter::DeliveryAgent class to perform mail delivery. You can also # use this script as a fully functioning mail filter. # # This script is a basic mail local delivery agent (DeliveryAgent) # that can be used in place of procmail, maildrop, etc. in a user's # .forward or .qmail file. The user supplies a delivery script that # is written in Ruby, which avoids the limitations of the crippled # mini-languages so often used in other DeliveryAgent programs. # # === Usage # # rdeliver is invoked from the command line using: # # % rdeliver [script] # # The script argument is optional. If omitted the script will look # for a file called .rdeliver in the home directory. # # Options are: # # --load-path:: Prepend the given directory to ruby's # load path. # # --log filename:: Log to the given filename. If no # log is specified, no logging occurs. # # --home directory:: Specify the home directory. # rdeliver will change to this directory # before reading and writing any files. # The home directory defaults to # the value of the +HOME+ or +LOGDIR+ # environment variable. # === Delivery Script # # The delivery script runs in the context of a class called Deliver # (in contrast, most ruby scripts run in the context of the Object # class). So any methods added with +def+ will be added to the # Deliver class. # # A minimal delivery script would be: # # def main # agent.save('inbox') # end # # This code defines a Deliver#main method that saves the mail into an # mbox style mailbox. # # The only API the Deliver script has is the #agent method. This # retrieves the RFilter::DeliveryAgent object associated with the current # message. Using the API of the RFilter::DeliveryAgent object, you can # access and modify the message body and headers, defer or reject the # message delivery, and deliver into various mailbox formats. # # See also RFilter::DeliveryAgent and Deliver. # # === Installation # # Assuming you have the RubyMail mail classes installed, you typically # have to put something like this in your .forward file: # # |"/usr/local/bin/rdeliver --log /home/you/.rlog" # # This will call rdeliver for each new message you get, and log to # /home/you/.rlog. # # === Catastrophic Errors # # The rdeliver script is very careful with errors. If there is any # problem, it logs the error to the log file you specify. But if you # do not specify a log file, or the error occurs before the log file # is opened, a record of the error is placed in a file called # CATASTROPHIC_DELIVERY_FAILURE in the home directory. If that fails, # the error information is printed to the standard output in the hopes # that it will be part of a bounce message. In all cases, the exit # code 75 is returned, which tells the MTA to re-try the delivery # again. def print_exception(file, exception) # :nodoc: file.puts("Exception:\n " + exception.inspect + "\n") file.puts "Backtrace:\n" exception.backtrace.each { |line| file.puts " " + line + "\n" } end # Try to get to home dir, in prep for possibly writing # CATASTROPHIC_DELIVERY_FAILURE not_in_home_dir_exception = nil begin Dir.chdir rescue Exception not_in_home_dir_exception = $! end begin $SAFE = 1 require 'getoptlong' parser = GetoptLong.new(['--load-path', '-I', GetoptLong::REQUIRED_ARGUMENT], ['--log', '-l', GetoptLong::REQUIRED_ARGUMENT], ['--home', '-h', GetoptLong::REQUIRED_ARGUMENT]) parser.quiet = true log = nil parser.each_option do |name, arg| case name when '--home' Dir.chdir(arg.dup.untaint) not_in_home_dir_exception = nil when '--log' log = arg.dup.untaint when '--load-path' $LOAD_PATH.unshift(arg.dup.untaint) else raise "don't know about argument #{name}" end end config = ARGV.shift config ||= '.rdeliver' config = File.expand_path(config).untaint raise "extra arguments passed to #{$0}: #{ARGV.inspect}" unless ARGV.empty? require 'rfilter/delivery_agent' begin # The Deliver class used by the bin/rdeliver.rb script to provide # a place for the user's delivery script to run. The user's # delivery script executes in the context of this class, so # methods defined with +def+ do not pollute the global namespace. # The user is expected to define a #main method that will be # called to deliver the mail. # # See also bin/rdeliver.rb class Deliver def initialize(agent) # :nodoc: @__MAIL_DELIVERY_AGENT__ = agent end # Return the RFilter::DeliveryAgent object for the current message. # This is all you need to retrieve, modify, and deliver the # message. def agent @__MAIL_DELIVERY_AGENT__ end # This method is called by bin/rdeliver.rb after the Deliver # object is instantiated. A default implementation that merely # defers the delivery is provided, but the user is expected to # replace this with a version that delivers the mail to the # proper location. def main agent.defer('no deliver method specified in configuration file') end end RFilter::DeliveryAgent.process(STDIN, log) { |agent| # IO.readlines used this way seems to have crapped out as of # ruby 1.8.1. # input = IO.readlines(config, nil)[0].untaint, input = File.new(config).read.untaint eval(input, Deliver.module_eval('binding()'), config) Deliver.new(agent).main } rescue RFilter::DeliveryAgent::DeliveryComplete => exception if (exception.is_a?(RFilter::DeliveryAgent::DeliveryDefer) || exception.is_a?(RFilter::DeliveryAgent::DeliveryReject)) && exception.message && exception.message.length > 0 puts exception.message end exit(RFilter::DeliveryAgent.exitcode(exception)) end raise "Script should never get here." rescue Exception => exception # The only way to get here is if something goes very wrong and # probably indicates a serious bug or configuration problem. if exception.class <= SystemExit raise exception # normal exit else # Be nice and stick the last delivery failure due to a # catastrophic situation in home/CATASTROPHIC_DELIVERY_FAILURE begin unless not_in_home_dir_exception.nil? raise not_in_home_dir_exception end File.open("CATASTROPHIC_DELIVERY_FAILURE", "w") { |file| print_exception(file, exception) case exception when LoadError file.puts("Current $LOAD_PATH: %s" % $LOAD_PATH.inspect) end } rescue Exception => another_exception # In the event that the above doesn't happen, we write the error # to stdout and hope the mailer includes it in the bounce that # will eventually occur. print_exception(STDOUT, exception) puts "Failed writing CATASTROPHIC_DELIVERY_FAILURE because:" print_exception(STDOUT, another_exception) ensure exit 75 # EX_TMPFAIL end end end rubyfilter-0.12.orig/bin/experimental-filter.rb0000444001013300101330000001463207773224251023250 0ustar yaegashiyaegashi00000000000000#!/usr/bin/env ruby #-- # Copyright (C) 2002 Matt Armstrong. All rights reserved. # # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # 3. The name of the author may not be used to endorse or promote # products derived from this software without specific prior # written permission. # # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE # GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER # IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR # OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN # IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # # The function daemon is covered by the copyright below, and was # fetched from # http://orange.kame.net/dev/cvsweb.cgi/kame/kame/kame/dtcp/dtcps.rb?rev=1.6 # # Copyright (C) 1999 WIDE Project. # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # 3. Neither the name of the project nor the names of its contributors # may be used to endorse or promote products derived from this software # without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS # OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF # SUCH DAMAGE. # def daemon(nochdir, noclose) pid = fork if pid == -1 return -1 elsif pid != nil exit 0 end Process.setsid() Dir.chdir('/') if (nochdir == 0) if noclose == 0 devnull = open("/dev/null", "r+") $stdin.reopen(devnull) $stdout.reopen(devnull) p = IO::pipe pid = fork if pid == -1 $stderr.reopen(devnull) elsif pid == nil p[1].close STDIN.reopen(p[0]) p[0].close else p[0].close $stderr.reopen(p[1]) p[1].close end end return 0 end $queue = "Maildir" $log = '.rfilter.log' require 'rfilter/delivery_agent' # The Deliver class used by the bin/rdeliver.rb script to provide # a place for the user's delivery script to run. The user's # delivery script executes in the context of this class, so # methods defined with +def+ do not pollute the global namespace. # The user is expected to define a #main method that will be # called to deliver the mail. # # See also bin/rdeliver.rb class Deliver def initialize(agent) # :nodoc: @__MAIL_DELIVERY_AGENT__ = agent end # Return the RFilter::DeliveryAgent object for the current message. # This is all you need to retrieve, modify, and deliver the message. def agent @__MAIL_DELIVERY_AGENT__ end # This method is called by bin/filter.rb after the Deliver object is # instantiated. A default implementation that merely defers the # delivery is provided, but the user is expected to replace this # with a version that delivers the mail to the proper location. def main agent.defer('no deliver method specified in configuration file') end end def queue_file_time(name) base = File.basename(name) first, = base.split('.') Integer(first) end def process_queue files = Dir[File.join($queue, 'new') + '/*'].sort { |a, b| queue_file_time(a) <=> queue_file_time(b) } files.each { |queue_file_name| begin File.open(queue_file_name) { |queue_file| RFilter::DeliveryAgent.process(queue_file, $log) { |agent| Deliver.new(agent).main } } rescue RFilter::DeliveryAgent::DeliverySuccess File::unlink(queue_file_name) rescue RFilter::DeliveryAgent::DeliveryReject # FIXME: for now, reject just drops the message on the floor File::unlink(queue_file_name) rescue RFilter::DeliveryAgent::DeliveryComplete # FIXME: truly defer somehow end } end def wait_for_more(max_minutes) max_seconds = max_minutes * 60 GC.start if sleep(max_seconds + 1) >= max_seconds # If we waited this long and there was no more mail, exit to free # up the system resources. exit end end def main # Read the .rdeliver file, which fills in the Deliver class. $config = '.rdeliver' # FIXME: make errors logged from this a lot more clear. eval(IO.readlines($config, nil)[0].untaint, Deliver.module_eval('binding()'), $config) begin toucher_thread = Thread.start { loop { now = Time.now File.utime(now, now, File.join("Maildir", ".filter.lock")) sleep(60) } } loop { process_queue sleep(15) } ensure Thread::kill(toucher_thread) if toucher_thread File::delete($lock_file_name) end end lockfile_name = File.join("Maildir", ".filter") if system('lockfile-create', '--retry', '1', lockfile_name) begin main ensure system('lockfile-remove', lockfile_name) end end rubyfilter-0.12.orig/bin/experimental-deliver.rb0000444001013300101330000001421307773224251023410 0ustar yaegashiyaegashi00000000000000#!/usr/bin/env ruby #-- # Copyright (C) 2002 Matt Armstrong. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # 3. The name of the author may not be used to endorse or promote # products derived from this software without specific prior # written permission. # # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE # GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER # IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR # OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN # IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # #++ # This script delivers a mail message taken from stdin to a mail # queue, and then starts the mail filter program if it isn't already # running. # # === Usage # # deliver.rb is invoked from the command line using: # # % deliver.rb # # Options are: # # --load-path:: Prepend the given directory to ruby's # load path. # # --log filename:: Log to the given filename. If no # log is specified, no logging occurs. # # --home directory:: Specify the home directory. # rdeliver will change to this directory # before reading and writing any files. # The home directory defaults to # the value of the +HOME+ or +LOGDIR+ # environment variable. # === Installation # # Assuming you have the RubyMail mail classes installed, you typically # have to put something like this in your .forward file: # # |"/usr/local/bin/deliver.rb --log /home/you/.rlog" # # This will call rdeliver for each new message you get, and log to # /home/you/.rlog. # # === Catastrophic Errors # # The deliver.rb script is very careful with errors. If there is any # problem, it logs the error to the log file you specify. But if you # do not specify a log file, or the error occurs before the log file # is opened, a record of the error is placed in a file called # CATASTROPHIC_DELIVERY_FAILURE in the home directory. If that fails, # the error information is printed to the standard output in the hopes # that it will be part of a bounce message. In all cases, the exit # code 75 is returned, which tells the MTA to re-try the delivery # again. def print_exception(file, exception) # :nodoc: file.puts("Exception:\n " + exception.inspect + "\n") file.puts "Backtrace:\n" exception.backtrace.each { |line| file.puts " " + line + "\n" } end def filter_pid(queue_dir) pid = nil begin File::open("Maildir/.filterpid") { |file| if Time.now - file.stat.mtime < (60 * 60 * 4) pid_str = file.gets.chomp.strip if pid_str =~ /^\d+/ pid = Integer(pid_str) end end } rescue Errno::ENOENT end return pid end def run_filter # FIXME: this is useful only for me. Hmm... exec("/home/matt/pkg/bin/ruby-cvs", '-I', '/home/matt/bk/live/rubyfilter', '-I', '/home/matt/bk/live/rubymail', "/home/matt/bk/live/rubyfilter/bin/filter.rb") end # Try to get to home dir, in preparation for possibly writing # CATASTROPHIC_DELIVERY_FAILURE not_in_home_dir_exception = nil begin Dir.chdir rescue Exception not_in_home_dir_exception = $! end begin $SAFE = 1 require 'getoptlong' parser = GetoptLong.new\ (['--load-path', '-I', GetoptLong::REQUIRED_ARGUMENT], ['--log', '-l', GetoptLong::REQUIRED_ARGUMENT], ['--home', '-h', GetoptLong::REQUIRED_ARGUMENT]) parser.quiet = true log = nil parser.each_option do |name, arg| case name when '--home' Dir.chdir(arg.untaint) not_in_home_dir_exception = nil when '--log' log = arg.untaint when '--load-path' $LOAD_PATH.unshift(arg.untaint) else raise "don't know about argument #{name}" end end raise "extra arguments passed to #{$0}: #{ARGV.inspect}" unless ARGV.empty? require 'rfilter/deliver' include RFilter::Deliver queue = "Maildir" deliver_maildir(queue, $stdin) fork { Process.setsid() File.umask(077) devnull = open('/dev/null', 'r+') # errors = open('/tmp/errors', 'w') $stdin.reopen(devnull) # $stdout.reopen(errors) # $stderr.reopen(errors) $stdout.reopen(devnull) $stderr.reopen(devnull) run_filter exit! } exit(0) rescue Exception => exception if exception.class <= SystemExit raise exception # normal exit else # Be nice and stick the last delivery failure due to a catastrophic # situation in home/CATASTROPHIC_DELIVERY_FAILURE begin unless not_in_home_dir_exception.nil? raise not_in_home_dir_exception end File.open("CATASTROPHIC_DELIVERY_FAILURE", "w") { |file| print_exception(file, exception) file.puts "LOAD_PATH" file.puts $LOAD_PATH.inspect } rescue Exception => another_exception # In the event that the above doesn't happen, we write the error # to stdout and hope the mailer includes it in the bounce that # will eventually occur. print_exception(STDOUT, exception) puts "Failed writing CATASTROPHIC_DELIVERY_FAILURE because:" print_exception(STDOUT, another_exception) ensure exit 75 # EX_TMPFAIL end end end rubyfilter-0.12.orig/doc/0000755001013300101330000000000007773224251016734 5ustar yaegashiyaegashi00000000000000rubyfilter-0.12.orig/doc/files/0000755001013300101330000000000007773224251020036 5ustar yaegashiyaegashi00000000000000rubyfilter-0.12.orig/doc/files/bin/0000755001013300101330000000000007773224251020606 5ustar yaegashiyaegashi00000000000000rubyfilter-0.12.orig/doc/files/bin/rdeliver_rb.html0000444001013300101330000001237507773224251024001 0ustar yaegashiyaegashi00000000000000 File: rdeliver.rb
rdeliver.rb
Path: bin/rdeliver.rb
Modified: Fri Dec 26 22:21:13 PST 2003

This script serves as an example of how you can use the RFilter::DeliveryAgent class to perform mail delivery. You can also use this script as a fully functioning mail filter.

This script is a basic mail local delivery agent (DeliveryAgent) that can be used in place of procmail, maildrop, etc. in a user’s .forward or .qmail file. The user supplies a delivery script that is written in Ruby, which avoids the limitations of the crippled mini-languages so often used in other DeliveryAgent programs.

Usage

rdeliver is invoked from the command line using:

  % rdeliver <options> [script]

The script argument is optional. If omitted the script will look for a file called .rdeliver in the home directory.

Options are:

—load-path:Prepend the given directory to ruby’s load path.
—log filename:Log to the given filename. If no log is specified, no logging occurs.
—home directory:Specify the home directory. rdeliver will change to this directory before reading and writing any files. The home directory defaults to the value of the HOME or LOGDIR environment variable.

Delivery Script

The delivery script runs in the context of a class called Deliver (in contrast, most ruby scripts run in the context of the Object class). So any methods added with def will be added to the Deliver class.

A minimal delivery script would be:

  def main
    agent.save('inbox')
  end

This code defines a Deliver#main method that saves the mail into an mbox style mailbox.

The only API the Deliver script has is the #agent method. This retrieves the RFilter::DeliveryAgent object associated with the current message. Using the API of the RFilter::DeliveryAgent object, you can access and modify the message body and headers, defer or reject the message delivery, and deliver into various mailbox formats.

See also RFilter::DeliveryAgent and Deliver.

Installation

Assuming you have the RubyMail mail classes installed, you typically have to put something like this in your .forward file:

   |"/usr/local/bin/rdeliver --log /home/you/.rlog"

This will call rdeliver for each new message you get, and log to /home/you/.rlog.

Catastrophic Errors

The rdeliver script is very careful with errors. If there is any problem, it logs the error to the log file you specify. But if you do not specify a log file, or the error occurs before the log file is opened, a record of the error is placed in a file called CATASTROPHIC_DELIVERY_FAILURE in the home directory. If that fails, the error information is printed to the standard output in the hopes that it will be part of a bounce message. In all cases, the exit code 75 is returned, which tells the MTA to re-try the delivery again.

Required files
getoptlong    rfilter/delivery_agent   
Classes and Modules
Class Deliver
rubyfilter-0.12.orig/doc/files/bin/rsendmail_rb.html0000444001013300101330000000671207773224251024141 0ustar yaegashiyaegashi00000000000000 File: rsendmail.rb
rsendmail.rb
Path: bin/rsendmail.rb
Modified: Thu Feb 06 19:50:55 PST 2003
Required files
rmail/parser    rmail/address    rmail/serialize    gdbm   
Methods
Public Instance methods
syslog(str)
get_recipients(message, field_name, list)
with_db(name) {|db| ...}

FIXME: share with .rdeliver

record_string_in_db(db, address)

FIXME: share with .rdeliver

rubyfilter-0.12.orig/doc/files/bin/experimental-deliver_rb.html0000444001013300101330000001116207773224251026303 0ustar yaegashiyaegashi00000000000000 File: experimental-deliver.rb
experimental-deliver.rb
Path: bin/experimental-deliver.rb
Modified: Thu Feb 06 19:50:02 PST 2003

This script delivers a mail message taken from stdin to a mail queue, and then starts the mail filter program if it isn’t already running.

Usage

deliver.rb is invoked from the command line using:

  % deliver.rb <options>

Options are:

—load-path:Prepend the given directory to ruby’s load path.
—log filename:Log to the given filename. If no log is specified, no logging occurs.
—home directory:Specify the home directory. rdeliver will change to this directory before reading and writing any files. The home directory defaults to the value of the HOME or LOGDIR environment variable.

Installation

Assuming you have the RubyMail mail classes installed, you typically have to put something like this in your .forward file:

   |"/usr/local/bin/deliver.rb --log /home/you/.rlog"

This will call rdeliver for each new message you get, and log to /home/you/.rlog.

Catastrophic Errors

The deliver.rb script is very careful with errors. If there is any problem, it logs the error to the log file you specify. But if you do not specify a log file, or the error occurs before the log file is opened, a record of the error is placed in a file called CATASTROPHIC_DELIVERY_FAILURE in the home directory. If that fails, the error information is printed to the standard output in the hopes that it will be part of a bounce message. In all cases, the exit code 75 is returned, which tells the MTA to re-try the delivery again.

Required files
getoptlong    rfilter/deliver   
Methods
Included modules
Public Instance methods
filter_pid(queue_dir)
run_filter()
rubyfilter-0.12.orig/doc/files/bin/experimental-deliver_rb.src/0000755001013300101330000000000007773224251026204 5ustar yaegashiyaegashi00000000000000rubyfilter-0.12.orig/doc/files/bin/experimental-deliver_rb.src/M000001.html0000444001013300101330000000205507773224251027727 0ustar yaegashiyaegashi00000000000000 filter_pid (bin/experimental-deliver.rb)
# File bin/experimental-deliver.rb, line 86
def filter_pid(queue_dir)
  pid = nil

  begin
    File::open("Maildir/.filterpid") { |file|
      if Time.now - file.stat.mtime < (60 * 60 * 4)
        pid_str = file.gets.chomp.strip
        if pid_str =~ /^\d+/
          pid = Integer(pid_str)
        end
      end
    }
  rescue Errno::ENOENT
  end
  return pid
end
rubyfilter-0.12.orig/doc/files/bin/experimental-deliver_rb.src/M000002.html0000444001013300101330000000162207773224251027727 0ustar yaegashiyaegashi00000000000000 run_filter (bin/experimental-deliver.rb)
# File bin/experimental-deliver.rb, line 103
def run_filter
  # FIXME: this is useful only for me.  Hmm...
  exec("/home/matt/pkg/bin/ruby-cvs",
       '-I', '/home/matt/bk/live/rubyfilter',
       '-I', '/home/matt/bk/live/rubymail',
       "/home/matt/bk/live/rubyfilter/bin/filter.rb")
end
rubyfilter-0.12.orig/doc/files/bin/rsendmail_rb.src/0000755001013300101330000000000007773224251024035 5ustar yaegashiyaegashi00000000000000rubyfilter-0.12.orig/doc/files/bin/rsendmail_rb.src/M000008.html0000444001013300101330000000122007773224251025560 0ustar yaegashiyaegashi00000000000000 syslog (bin/rsendmail.rb)
# File bin/rsendmail.rb, line 34
def syslog(str)
  system('logger', '-p', 'mail.info', '-t', 'rsendmail', str.to_s)
end
rubyfilter-0.12.orig/doc/files/bin/rsendmail_rb.src/M000009.html0000444001013300101330000000150407773224251025566 0ustar yaegashiyaegashi00000000000000 get_recipients (bin/rsendmail.rb)
# File bin/rsendmail.rb, line 72
def get_recipients(message, field_name, list)
  unless message.header[field_name].nil?
    RMail::Address.parse(message.header[field_name]).each do |address|
      # FIXME: need an "smtpaddress" method
      list.push(address.address)
    end
  end
end
rubyfilter-0.12.orig/doc/files/bin/rsendmail_rb.src/M000010.html0000444001013300101330000000206607773224251025562 0ustar yaegashiyaegashi00000000000000 with_db (bin/rsendmail.rb)
# File bin/rsendmail.rb, line 92
def with_db(name)
  require 'gdbm'
  begin
    db = nil
    begin
      db = GDBM::open(File.join("/home/matt/.rfilter/var", name), 0600)
    rescue Errno::EWOULDBLOCK
      # FIXME: only wait so long, then defer
      sleep(2)
      retry
    end
    yield db
  ensure
    db.close unless db.nil?
  end
end
rubyfilter-0.12.orig/doc/files/bin/rsendmail_rb.src/M000011.html0000444001013300101330000000142007773224251025554 0ustar yaegashiyaegashi00000000000000 record_string_in_db (bin/rsendmail.rb)
# File bin/rsendmail.rb, line 110
def record_string_in_db(db, address)
  record = db[address]
  count = record.split(/:/)[1] unless record.nil?
  count ||= '0'
  db[address] = Time.now.strftime("%Y%m%d") + ':' + count.succ
end
rubyfilter-0.12.orig/doc/files/bin/experimental-filter_rb.src/0000755001013300101330000000000007773224251026037 5ustar yaegashiyaegashi00000000000000rubyfilter-0.12.orig/doc/files/bin/experimental-filter_rb.src/M000003.html0000444001013300101330000000262307773224251027565 0ustar yaegashiyaegashi00000000000000 daemon (bin/experimental-filter.rb)
# File bin/experimental-filter.rb, line 63
def daemon(nochdir, noclose)
  pid = fork
  if pid == -1
    return -1
  elsif pid != nil
    exit 0
  end

  Process.setsid()

  Dir.chdir('/') if (nochdir == 0)
  if noclose == 0
    devnull = open("/dev/null", "r+")
    $stdin.reopen(devnull)
    $stdout.reopen(devnull)
    p = IO::pipe
    pid = fork
    if pid == -1
      $stderr.reopen(devnull)
    elsif pid == nil
      p[1].close
      STDIN.reopen(p[0])
      p[0].close
    else
      p[0].close
      $stderr.reopen(p[1])
      p[1].close
    end
  end
  return 0
end
rubyfilter-0.12.orig/doc/files/bin/experimental-filter_rb.src/M000004.html0000444001013300101330000000113207773224251027560 0ustar yaegashiyaegashi00000000000000 queue_file_time (bin/experimental-filter.rb)
# File bin/experimental-filter.rb, line 126
def queue_file_time(name)
  base = File.basename(name)
  first, = base.split('.')
  Integer(first)
end
rubyfilter-0.12.orig/doc/files/bin/experimental-filter_rb.src/M000005.html0000444001013300101330000000254407773224251027571 0ustar yaegashiyaegashi00000000000000 process_queue (bin/experimental-filter.rb)
# File bin/experimental-filter.rb, line 132
def process_queue
  files = Dir[File.join($queue, 'new') + '/*'].sort { |a, b|
    queue_file_time(a) <=> queue_file_time(b)
  }
  files.each { |queue_file_name|
    begin
      File.open(queue_file_name) { |queue_file|
        RFilter::DeliveryAgent.process(queue_file, $log) { |agent|
          Deliver.new(agent).main
        }
      }
    rescue RFilter::DeliveryAgent::DeliverySuccess
      File::unlink(queue_file_name)
    rescue RFilter::DeliveryAgent::DeliveryReject
      # FIXME: for now, reject just drops the message on the floor
      File::unlink(queue_file_name)
    rescue RFilter::DeliveryAgent::DeliveryComplete
      # FIXME: truly defer somehow
    end
  }
end
rubyfilter-0.12.orig/doc/files/bin/experimental-filter_rb.src/M000006.html0000444001013300101330000000145307773224251027570 0ustar yaegashiyaegashi00000000000000 wait_for_more (bin/experimental-filter.rb)
# File bin/experimental-filter.rb, line 154
def wait_for_more(max_minutes)
  max_seconds = max_minutes * 60
  GC.start
  if sleep(max_seconds + 1) >= max_seconds
    # If we waited this long and there was no more mail, exit to free
    # up the system resources.
    exit
  end
end
rubyfilter-0.12.orig/doc/files/bin/experimental-filter_rb.src/M000007.html0000444001013300101330000000250507773224251027570 0ustar yaegashiyaegashi00000000000000 main (bin/experimental-filter.rb)
# File bin/experimental-filter.rb, line 164
def main

  # Read the .rdeliver file, which fills in the Deliver class.
  $config = '.rdeliver'

  # FIXME: make errors logged from this a lot more clear.
  eval(IO.readlines($config, nil)[0].untaint,
       Deliver.module_eval('binding()'),
       $config)

  begin
    toucher_thread = Thread.start {
      loop {
        now = Time.now
        File.utime(now, now, File.join("Maildir", ".filter.lock"))
        sleep(60)
      }
    }
    loop {
      process_queue
      sleep(15)
    }
  ensure
    Thread::kill(toucher_thread) if toucher_thread
    File::delete($lock_file_name)
  end
end
rubyfilter-0.12.orig/doc/files/bin/experimental-filter_rb.html0000444001013300101330000001352207773224251026140 0ustar yaegashiyaegashi00000000000000 File: experimental-filter.rb
experimental-filter.rb
Path: bin/experimental-filter.rb
Modified: Thu Feb 06 19:50:22 PST 2003

Read the .rdeliver file, which fills in the Deliver class.

Required files
rfilter/delivery_agent   
Methods
Classes and Modules
Class Deliver
Public Instance methods
daemon(nochdir, noclose)

The function daemon is covered by the copyright below, and was fetched from orange.kame.net/dev/cvsweb.cgi/kame/kame/kame/dtcp/dtcps.rb?rev=1.6

Copyright (C) 1999 WIDE Project. All rights reserved.

Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

  1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
  2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
  3. Neither the name of the project nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS’’ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

queue_file_time(name)
process_queue()
wait_for_more(max_minutes)
main()
rubyfilter-0.12.orig/doc/files/lib/0000755001013300101330000000000007773224251020604 5ustar yaegashiyaegashi00000000000000rubyfilter-0.12.orig/doc/files/lib/rfilter/0000755001013300101330000000000007773224251022253 5ustar yaegashiyaegashi00000000000000rubyfilter-0.12.orig/doc/files/lib/rfilter/keyed_mailbox_rb.html0000444001013300101330000000761607773224251026450 0ustar yaegashiyaegashi00000000000000 File: keyed_mailbox.rb
keyed_mailbox.rb
Path: lib/rfilter/keyed_mailbox.rb
Modified: Fri Feb 14 07:53:44 PST 2003
Required files
rfilter/deliver    digest/md5    timeout   
Classes and Modules
rubyfilter-0.12.orig/doc/files/lib/rfilter/deliver_rb.html0000444001013300101330000001244707773224251025264 0ustar yaegashiyaegashi00000000000000 File: deliver.rb
deliver.rb
Path: lib/rfilter/deliver.rb
Modified: Wed Sep 17 10:24:54 PDT 2003
  Copyright (C) 2001, 2002, 2003 Matt Armstrong.  All rights reserved.

Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

  1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
  2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
  3. The name of the author may not be used to endorse or promote products derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS’’ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

Required files
socket   
Classes and Modules
rubyfilter-0.12.orig/doc/files/lib/rfilter/tagged_rb.html0000444001013300101330000001246507773224251025065 0ustar yaegashiyaegashi00000000000000 File: tagged.rb
tagged.rb
Path: lib/rfilter/tagged.rb
Modified: Thu Feb 06 20:56:19 PST 2003
  Copyright (C) 2002 Matt Armstrong.  All rights reserved.

Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

  1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
  2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
  3. The name of the author may not be used to endorse or promote products derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS’’ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

Required files
mail/address    hmac-sha1   
Classes and Modules
rubyfilter-0.12.orig/doc/files/lib/rfilter/mta_rb.html0000444001013300101330000001214407773224251024405 0ustar yaegashiyaegashi00000000000000 File: mta.rb
mta.rb
Path: lib/rfilter/mta.rb
Modified: Thu Feb 06 20:56:02 PST 2003
  Copyright (C) 2001 Matt Armstrong.  All rights reserved.

Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

  1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
  2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
  3. The name of the author may not be used to endorse or promote products derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS’’ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

Classes and Modules
rubyfilter-0.12.orig/doc/files/lib/rfilter/delivery_agent_rb.html0000444001013300101330000001262307773224251026627 0ustar yaegashiyaegashi00000000000000 File: delivery_agent.rb
delivery_agent.rb
Path: lib/rfilter/delivery_agent.rb
Modified: Thu Feb 06 21:01:19 PST 2003
  Copyright (C) 2001, 2002, 2003 Matt Armstrong.  All rights reserved.

Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

  1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
  2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
  3. The name of the author may not be used to endorse or promote products derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS’’ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

Required files
rmail/message    rmail/parser    rfilter/deliver    rfilter/mta   
Classes and Modules
rubyfilter-0.12.orig/doc/files/NEWS.html0000444001013300101330000000746307773224251021510 0ustar yaegashiyaegashi00000000000000 File: NEWS
NEWS
Path: NEWS
Modified: Fri Dec 26 22:36:31 PST 2003

Changes in RubyFilter 0.12 (released 2003-12-26)

  • Ruby 1.8.1 compatability changes.

Changes in RubyFilter 0.11 (released 2003-09-17)

  • Ruby 1.8.0 compatability changes.
  • No longer verify that a unix mbox file is in the correct format before appending to it.

Changes in RubyFilter 0.10 (released 2003-02-04)

  • Add RFilter::Deliver.write_to_mbox that writes to an already open mailbox file. This can be used when none of the anal checks, locking, etc. that RFilter::Deliver.deliver_mbox does is desireable.
  • Add RFilter::KeyedMailbox#each_key and RFilter::KeyedMailbox#key_mtime methods that can be used to implement expiry when you want to process the message before it is deleted.
  • DeliveryAgent#deliver_mbox now ignores the SIGXFSZ exception and recovers from the EFBIG Errno exception.

Changes in RubyFilter 0.9 (released 2002-01-16)

  • New DeliveryAgent#filter method that can be used to filter a message through an external command, possibly changing the message.
  • DeliveryAgent::DeliveryPipeFailure renamed to DeliveryAgent::DeliveryCommandFailure since it is now raised by DeliveryAgent#pipe as well as DeliveryAgent#filter.
  • DeliveryAgent#defer and DeliveryAgent#reject now have a default argument for the message string.
  • Other changes to bring up to date with current RubyMail and Ruby 1.8.0.

Changes in RubyFilter 0.8 (released 2002-03-18)

  • Created from RubyMail 0.7
  • All Mail::* constants moved into the RFilter:: module.
  • Mail::Deliver.deliver_mbox returns the name of the file delivered to.
  • rdeliver.rb now prints DeliveryDever and DeliveryReject messages to stdout in the hopes that the MTA will include them in the bounce message.
  • Renamed Mail::LDA to RFilter::DeliveryAgent.
  • RFilter::Deliver.deliver_maildir — bring in line with the Maildir delivery spec (i.e. stat the tmp file before trying to open it instead of relying on O_EXCL).
rubyfilter-0.12.orig/doc/files/README.html0000444001013300101330000001017507773224251021663 0ustar yaegashiyaegashi00000000000000 File: README
README
Path: README
Modified: Fri Feb 07 13:38:21 PST 2003

RubyFilter

This is a framework for filtering mail, possibly modifying it, and delivering it to various mailbox formats.

RubyFilter is available at:

    http://www.lickey.com/rubyfilter/

RubyFilter depends on RubyMail, available at:

    http://www.lickey.com/rubymail/

Why?

The world needs alternatives to procmail. I wanted one that allowed me to write a mail filter in a fully capable scripting language.

Status

This package is currently very raw. All API is subject to change. I very much appreciate suggestions and comments.

However, I do use this for all of my own mail filtering.

Requirements

Ruby 1.6.* or Ruby 1.8.*. Only tested under Linux, should be fine under any Unix.

Documentation

See the doc/ subdirectory for HTML documentation.

Install

Type the following while in the package directory:

  ruby install.rb config
  ruby install.rb setup
  ruby install.rb install

You may need special permissions to execute the last line. If you want to just install RubyMail to a custom location, just copy the rmail subdirectory manually.

Tests?

This package has a complete unit test suite (requires RubyUnit to run).

License

Copyright © 2003 Matt Armstrong. All rights reserved.

Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

  1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
  2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
  3. The name of the author may not be used to endorse or promote products derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS’’ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

Support

To reach the author of RubyFilter, send mail to matt@lickey.com.

rubyfilter-0.12.orig/doc/files/THANKS.html0000444001013300101330000000347607773224251021724 0ustar yaegashiyaegashi00000000000000 File: THANKS
THANKS
Path: THANKS
Modified: Wed Sep 17 09:26:57 PDT 2003

Thanks to Mail::Audit for:

  • Showing that reasonable mail filters can be written in a scripting language.

Thanks to TMDA for:

  • Showing that an aggressive SPAM heuristic coupled with a confirmation system is an effective technique for reducing SPAM.
  • The idea of tacking on an HMAC to address extensions (Mail::AddressTagger).

Joakim Andersson - Initial implementation of the filter method. Pavel Kolar - but report (1.8.0 incompatability)

rubyfilter-0.12.orig/doc/index.html0000444001013300101330000000126707773224251020735 0ustar yaegashiyaegashi00000000000000 RubyFilter Documentation (version 0.12) <body bgcolor="white"> Sorry, RDoc currently only generates HTML using frames. </body> rubyfilter-0.12.orig/doc/rdoc-style.css0000444001013300101330000000340307773224251021531 0ustar yaegashiyaegashi00000000000000 body,td,p { font-family: Verdana, Arial, Helvetica, sans-serif; color: #000040; } .attr-rw { font-size: x-small; color: #444488 } .title-row { background: #0000aa; color: #eeeeff; } .big-title-font { color: white; font-family: Verdana, Arial, Helvetica, sans-serif; font-size: large; height: 50px} .small-title-font { color: aqua; font-family: Verdana, Arial, Helvetica, sans-serif; font-size: xx-small; } .aqua { color: aqua } .method-name, attr-name { font-family: monospace; font-weight: bold; } .tablesubtitle, .tablesubsubtitle { width: 100%; margin-top: 1ex; margin-bottom: .5ex; padding: 5px 0px 5px 20px; font-size: large; color: aqua; background: #3333cc; } .name-list { font-family: monospace; margin-left: 40px; margin-bottom: 2ex; line-height: 140%; } .description { margin-left: 40px; margin-top: -2ex; margin-bottom: 2ex; } .description p { line-height: 140%; } .aka { margin-left: 40px; margin-bottom: 2ex; line-height: 100%; font-size: small; color: #808080; } .methodtitle { font-size: medium; text-decoration: none; color: #0000AA; background: white; } .paramsig { font-size: small; } .srcbut { float: right } pre { font-size: 1.2em; } tt { font-size: 1.2em; } pre.source { border-style: groove; background-color: #ddddff; margin-left: 40px; padding: 1em 0em 1em 2em; } .classlist { margin-left: 40px; margin-bottom: 2ex; line-height: 140%; } li { display: list-item; margin-top: .6em; } .kw { color: #3333FF; font-weight: bold } .cmt { color: green; font-style: italic } .str { color: #662222; font-style: italic } .re { color: #662222; }rubyfilter-0.12.orig/doc/fr_method_index.html0000444001013300101330000001337707773224251022771 0ustar yaegashiyaegashi00000000000000 Methods agent (Deliver)
agent (Deliver)
body (RFilter::DeliveryAgent)
daemon (bin/experimental-filter.rb)
dated (RFilter::AddressTagger)
defer (RFilter::DeliveryAgent)
delete (RFilter::KeyedMailbox)
deliver_filter (RFilter::Deliver)
deliver_maildir (RFilter::Deliver)
deliver_mbox (RFilter::Deliver)
deliver_pipe (RFilter::Deliver)
each_key (RFilter::KeyedMailbox)
exitcode (RFilter::DeliveryAgent)
expire (RFilter::KeyedMailbox)
filter (RFilter::DeliveryAgent)
filter_pid (bin/experimental-deliver.rb)
get_recipients (bin/rsendmail.rb)
header (RFilter::DeliveryAgent)
key_mtime (RFilter::KeyedMailbox)
keyword (RFilter::AddressTagger)
log (RFilter::DeliveryAgent)
logging_level (RFilter::DeliveryAgent)
logging_level= (RFilter::DeliveryAgent)
main (Deliver)
main (Deliver)
main (bin/experimental-filter.rb)
message (RFilter::DeliveryAgent)
message= (RFilter::DeliveryAgent)
new (RFilter::AddressTagger)
new (RFilter::DeliveryAgent::DeliveryCommandFailure)
new (RFilter::DeliveryAgent::DeliveryReject)
new (RFilter::DeliveryAgent::DeliverySuccess)
new (RFilter::DeliveryAgent::DeliveryDefer)
new (RFilter::DeliveryAgent::LoggingError)
new (RFilter::DeliveryAgent)
new (RFilter::KeyedMailbox)
new (RFilter::DeliveryAgent::DeliveryComplete)
path (RFilter::KeyedMailbox)
pipe (RFilter::DeliveryAgent)
process (RFilter::DeliveryAgent)
process_queue (bin/experimental-filter.rb)
queue_file_time (bin/experimental-filter.rb)
record_string_in_db (bin/rsendmail.rb)
reject (RFilter::DeliveryAgent)
retrieve (RFilter::KeyedMailbox)
run_filter (bin/experimental-deliver.rb)
save (RFilter::KeyedMailbox)
save (RFilter::DeliveryAgent)
syslog (bin/rsendmail.rb)
verify (RFilter::AddressTagger)
wait_for_more (bin/experimental-filter.rb)
with_db (bin/rsendmail.rb)
write_to_mbox (RFilter::Deliver)
rubyfilter-0.12.orig/doc/fr_class_index.html0000444001013300101330000000432507773224251022607 0ustar yaegashiyaegashi00000000000000 Classes Deliver
RFilter
RFilter::AddressTagger
RFilter::Deliver
RFilter::Deliver::DeliveryError
RFilter::Deliver::LockingError
RFilter::Deliver::NotAFile
RFilter::Deliver::NotAMailbox
RFilter::DeliveryAgent
RFilter::DeliveryAgent::DeliveryCommandFailure
RFilter::DeliveryAgent::DeliveryComplete
RFilter::DeliveryAgent::DeliveryDefer
RFilter::DeliveryAgent::DeliveryReject
RFilter::DeliveryAgent::DeliverySuccess
RFilter::DeliveryAgent::LoggingError
RFilter::KeyedMailbox
RFilter::MTA
rubyfilter-0.12.orig/doc/fr_file_index.html0000444001013300101330000000310107773224251022410 0ustar yaegashiyaegashi00000000000000 Files NEWS
README
THANKS
bin/experimental-deliver.rb
bin/experimental-filter.rb
bin/rdeliver.rb
bin/rsendmail.rb
lib/rfilter/deliver.rb
lib/rfilter/delivery_agent.rb
lib/rfilter/keyed_mailbox.rb
lib/rfilter/mta.rb
lib/rfilter/tagged.rb
rubyfilter-0.12.orig/doc/created.rid0000444001013300101330000000003507773224251021037 0ustar yaegashiyaegashi00000000000000Fri Dec 26 22:37:28 PST 2003 rubyfilter-0.12.orig/doc/classes/0000755001013300101330000000000007773224251020371 5ustar yaegashiyaegashi00000000000000rubyfilter-0.12.orig/doc/classes/RFilter/0000755001013300101330000000000007773224251021740 5ustar yaegashiyaegashi00000000000000rubyfilter-0.12.orig/doc/classes/RFilter/DeliveryAgent.src/0000755001013300101330000000000007773224251025270 5ustar yaegashiyaegashi00000000000000rubyfilter-0.12.orig/doc/classes/RFilter/DeliveryAgent.src/M000029.html0000444001013300101330000000177107773224251027031 0ustar yaegashiyaegashi00000000000000 new (RFilter::DeliveryAgent)
# File lib/rfilter/delivery_agent.rb, line 110
    def initialize(input, logfile)
      @logfile =
        if logfile.nil?
          nil
        else
          File.open(logfile, File::CREAT|File::APPEND|File::WRONLY, 0600)
        end
      @message = if input.is_a?(RMail::Message)
                   input
                 else
                   RMail::Parser.new.parse(input)
                 end
      @logging_level = 2
    end
rubyfilter-0.12.orig/doc/classes/RFilter/DeliveryAgent.src/M000030.html0000444001013300101330000000173707773224251027023 0ustar yaegashiyaegashi00000000000000 save (RFilter::DeliveryAgent)
# File lib/rfilter/delivery_agent.rb, line 140
    def save(folder, continue = false)
      log(2, "Action: save to #{folder.inspect}")
      retval = if folder =~ %{(.*[^/])/$}
                 deliver_maildir($1, @message)
               else
                 deliver_mbox(folder, @message)
               end
      raise DeliverySuccess, "saved to mbox #{folder.inspect}" unless continue
      retval
    end
rubyfilter-0.12.orig/doc/classes/RFilter/DeliveryAgent.src/M000031.html0000444001013300101330000000121307773224251027011 0ustar yaegashiyaegashi00000000000000 reject (RFilter::DeliveryAgent)
# File lib/rfilter/delivery_agent.rb, line 153
    def reject(reason = nil)
      log(2, "Action: reject: " + reason.to_s)
      raise DeliveryReject.new(reason.to_s)
    end
rubyfilter-0.12.orig/doc/classes/RFilter/DeliveryAgent.src/M000032.html0000444001013300101330000000120707773224251027015 0ustar yaegashiyaegashi00000000000000 defer (RFilter::DeliveryAgent)
# File lib/rfilter/delivery_agent.rb, line 161
    def defer(reason = nil)
      log(2, "Action: defer: " + reason.to_s)
      raise DeliveryDefer.new(reason.to_s)
    end
rubyfilter-0.12.orig/doc/classes/RFilter/DeliveryAgent.src/M000033.html0000444001013300101330000000202207773224251027012 0ustar yaegashiyaegashi00000000000000 pipe (RFilter::DeliveryAgent)
# File lib/rfilter/delivery_agent.rb, line 177
    def pipe(command, continue = false)
      log(2, "Action: pipe to #{command.inspect}")
      deliver_pipe(command, @message)
      if $? != 0
        m = "pipe failed for command #{command.inspect}"
        log(1, "Error: " + m)
        raise DeliveryCommandFailure.new(m, $?)
      end
      unless continue
        raise DeliverySuccess.new("pipe to #{command.inspect}")
      end
    end
rubyfilter-0.12.orig/doc/classes/RFilter/DeliveryAgent.src/M000034.html0000444001013300101330000000206707773224251027024 0ustar yaegashiyaegashi00000000000000 filter (RFilter::DeliveryAgent)
# File lib/rfilter/delivery_agent.rb, line 199
    def filter(*command)
      log(2, "Action: filter through #{command.inspect}")
      msg = nil
      status = deliver_filter(@message, *command) { |io|
        msg = RMail::Parser.new.parse(io)
      }
      if status != 0
        m = format("filter failed for command %s (status %s)",
                   command.inspect, status.inspect)
        log(1, "Error: " + m)
        raise DeliveryCommandFailure.new(m, status)
      end
      @message = msg
    end
rubyfilter-0.12.orig/doc/classes/RFilter/DeliveryAgent.src/M000035.html0000444001013300101330000000255707773224251027031 0ustar yaegashiyaegashi00000000000000 log (RFilter::DeliveryAgent)
# File lib/rfilter/delivery_agent.rb, line 219
    def log(level, str)
      if level <= 0 and @logfile.nil?
        raise LoggingError, "failed to log high priority message: #{str}"
      end
      return if @logfile.nil? or level > @logging_level
      begin
        @logfile.flock(File::LOCK_EX)
        @logfile.print(Time.now.strftime("%Y/%m/%d %H:%M:%S "))
        @logfile.print(sprintf("%05d: ", Process.pid))
        @logfile.puts(str)
        @logfile.flush
        @logfile.flock(File::LOCK_UN)
      rescue
        # FIXME: this isn't tested
        raise LoggingError.new("failed to log message: #{str}", $!)
      end
    end
rubyfilter-0.12.orig/doc/classes/RFilter/DeliveryAgent.src/M000036.html0000444001013300101330000000101207773224251027013 0ustar yaegashiyaegashi00000000000000 logging_level (RFilter::DeliveryAgent)
# File lib/rfilter/delivery_agent.rb, line 240
    def logging_level
      @logging_level
    end
rubyfilter-0.12.orig/doc/classes/RFilter/DeliveryAgent.src/M000037.html0000444001013300101330000000125407773224251027024 0ustar yaegashiyaegashi00000000000000 logging_level= (RFilter::DeliveryAgent)
# File lib/rfilter/delivery_agent.rb, line 248
    def logging_level=(level)
      level = Integer(level)
      raise ArgumentError, "invalid logging level value #{level}" if level < 1
      @logging_level = level
    end
rubyfilter-0.12.orig/doc/classes/RFilter/DeliveryAgent.src/M000038.html0000444001013300101330000000077007773224251027027 0ustar yaegashiyaegashi00000000000000 message (RFilter::DeliveryAgent)
# File lib/rfilter/delivery_agent.rb, line 258
    def message
      @message
    end
rubyfilter-0.12.orig/doc/classes/RFilter/DeliveryAgent.src/M000039.html0000444001013300101330000000101507773224251027021 0ustar yaegashiyaegashi00000000000000 message= (RFilter::DeliveryAgent)
# File lib/rfilter/delivery_agent.rb, line 264
    def message=(message)
      @message = message
    end
rubyfilter-0.12.orig/doc/classes/RFilter/DeliveryAgent.src/M000040.html0000444001013300101330000000077507773224251027025 0ustar yaegashiyaegashi00000000000000 header (RFilter::DeliveryAgent)
# File lib/rfilter/delivery_agent.rb, line 272
    def header
      @message.header
    end
rubyfilter-0.12.orig/doc/classes/RFilter/DeliveryAgent.src/M000041.html0000444001013300101330000000076707773224251027027 0ustar yaegashiyaegashi00000000000000 body (RFilter::DeliveryAgent)
# File lib/rfilter/delivery_agent.rb, line 280
    def body
      @message.body
    end
rubyfilter-0.12.orig/doc/classes/RFilter/DeliveryAgent.src/M000042.html0000444001013300101330000000405707773224251027024 0ustar yaegashiyaegashi00000000000000 process (RFilter::DeliveryAgent)
# File lib/rfilter/delivery_agent.rb, line 309
      def process(input, logfile)
        begin
          lda = RFilter::DeliveryAgent.new(input, logfile)
          yield lda
          lda.defer("finished without a final delivery")
        rescue Exception => exception
          if exception.class <= DeliveryComplete
            raise exception
          else
            begin
              lda.log(0, "uncaught exception: " + exception.inspect)
              lda.log(0, "uncaught exception backtrace:\n    " +
                      exception.backtrace.join("\n    "))
              lda.defer("uncaught exception")
            rescue Exception
              if $!.class <= DeliveryComplete
                # The lda.defer above will generate this, just re-raise
                # the delivery status exception.
                raise
              else
                # Any errors logging in the uncaught exception and we
                # just re-raise the original exception
                raise exception
              end
            end
          end
        end
      end
rubyfilter-0.12.orig/doc/classes/RFilter/DeliveryAgent.src/M000043.html0000444001013300101330000000211607773224251027017 0ustar yaegashiyaegashi00000000000000 exitcode (RFilter::DeliveryAgent)
# File lib/rfilter/delivery_agent.rb, line 342
      def exitcode(exception)
        case exception
        when DeliverySuccess
          RFilter::MTA::EXITCODE_DELIVERED
        when DeliveryReject
          RFilter::MTA::EXITCODE_REJECT
        when DeliveryComplete
          RFilter::MTA::EXITCODE_DEFER
        else
          raise ArgumentError,
            "argument is not a DeliveryComplete exception: " +
            "#{exception.inspect} (#{exception.class})"
        end
      end
rubyfilter-0.12.orig/doc/classes/RFilter/Deliver.src/0000755001013300101330000000000007773224251024120 5ustar yaegashiyaegashi00000000000000rubyfilter-0.12.orig/doc/classes/RFilter/Deliver.src/M000016.html0000444001013300101330000000507407773224251025655 0ustar yaegashiyaegashi00000000000000 deliver_mbox (RFilter::Deliver)
# File lib/rfilter/deliver.rb, line 63
    def deliver_mbox(filename, message)
      return filename if filename == '/dev/null'
      File.open(filename,
                File::APPEND|File::WRONLY|File::CREAT|SYNC_IF_NO_FSYNC,
                0600) { |f|
        max = 5
        max.times { |i|
          break if f.flock(File::LOCK_EX | File::LOCK_NB)
          raise LockingError, "Timeout locking mailbox." if i == max - 1
          sleep(1)
        }
        st = f.lstat
        unless st.file?
          raise NotAFile,
            "Can not deliver to #{filename}, not a regular file."
        end
        begin
          # Ignore SIGXFSZ, since we want to get the Errno::EFBIG
          # exception when the file is too big.
          old_handler = trap('XFSZ', 'IGNORE') || 'DEFAULT'
          write_to_mbox(f, message)
          begin
            f.fsync
          rescue NameError
            # NameError happens with older versions of Ruby that have
            # no File#fsync
            f.flush
          end
        rescue Exception => e
          begin
            begin
              f.flush
            rescue Exception
            end
            f.truncate(st.size)
          ensure
            raise e
          end
        ensure
          if old_handler
            trap('XFSZ', old_handler)
          end
        end
        f.flock(File::LOCK_UN)
      }
      filename
    end
rubyfilter-0.12.orig/doc/classes/RFilter/Deliver.src/M000017.html0000444001013300101330000000251007773224251025646 0ustar yaegashiyaegashi00000000000000 write_to_mbox (RFilter::Deliver)
# File lib/rfilter/deliver.rb, line 115
    def write_to_mbox(output_io, message)
      first = true
      message.each { |line|
        if first
          first = false
          if line !~ /^From .*\d$/
            from = "From foo@bar  " + Time.now.asctime + "\n"
            output_io << from
          end
        elsif line =~ /^From /
          output_io << '>'
        end
        output_io << line
        output_io << "\n" unless line[-1] == ?\n
      }
      output_io << "\n"
    end
rubyfilter-0.12.orig/doc/classes/RFilter/Deliver.src/M000018.html0000444001013300101330000000165307773224251025656 0ustar yaegashiyaegashi00000000000000 deliver_pipe (RFilter::Deliver)
# File lib/rfilter/deliver.rb, line 147
    def deliver_pipe(command, message)
      begin
        IO.popen(command, "w") { |io|
          message.each { |line|
            io << line
            io << "\n" unless line[-1] == ?\n
          }
        }
      rescue Errno::EPIPE
        # Just ignore.
      end
    end
rubyfilter-0.12.orig/doc/classes/RFilter/Deliver.src/M000019.html0000444001013300101330000000374107773224251025657 0ustar yaegashiyaegashi00000000000000 deliver_filter (RFilter::Deliver)
# File lib/rfilter/deliver.rb, line 172
    def deliver_filter(message, *command)
      begin
        to_r, to_w = IO.pipe
        from_r, from_w = IO.pipe
        if pid = fork
          # parent
          to_r.close
          from_w.close
          writer = Thread::new {
            message.each { |line|
              to_w << line
              to_w << "\n" unless line[-1] == ?\n
            }
            to_w.close
          }
          yield from_r
        else
          # child
          begin
            to_w.close
            from_r.close
            STDIN.reopen(to_r)
            to_r.close
            STDOUT.reopen(from_w)
            from_w.close
            exec(*command)
          ensure
            exit!
          end
        end
      ensure
        writer.kill if writer and writer.alive?
        [ to_r, to_w, from_r, from_w ].each { |io|
          if io && !io.closed?
            begin
              io.close
            rescue Errno::EPIPE
            end
          end
        }
      end
      Process.waitpid2(pid, 0)[1]
    end
rubyfilter-0.12.orig/doc/classes/RFilter/Deliver.src/M000020.html0000444001013300101330000000625107773224251025646 0ustar yaegashiyaegashi00000000000000 deliver_maildir (RFilter::Deliver)
# File lib/rfilter/deliver.rb, line 233
    def deliver_maildir(dir, message)
      require 'socket'

      # First, make the required directories
      new = File.join(dir, 'new')
      tmp = File.join(dir, 'tmp')
      [ dir, new, tmp, File.join(dir, 'cur') ].each { |d|
        begin
          Dir.mkdir(d, 0700)
        rescue Errno::EEXIST
          raise unless FileTest::directory?(d)
        end
      }

      sequence = @@mail_deliver_maildir_count
      @@mail_deliver_maildir_count = @@mail_deliver_maildir_count.next
      tmp_name = nil
      new_name = nil
      hostname = Socket::gethostname.gsub(/[^\w]/, '_').untaint
      pid = Process::pid
      3.times { |i|
        now = Time::now
        name = sprintf("%d.M%XP%dQ%d.%s",
                       Time::now.tv_sec, Time::now.tv_usec,
                       pid, sequence, hostname)
        tmp_name = File.join(tmp, name)
        new_name = File.join(new, name)
        begin
          File::stat(tmp_name)
        rescue Errno::ENOENT
          break
        rescue Exception
          raise if i == 2
        end
        raise "Too many tmp file conflicts." if i == 2
        sleep(2)
      }

      begin
        File.open(tmp_name,
                  File::CREAT|File::EXCL|File::WRONLY|SYNC_IF_NO_FSYNC,
                  0600) { |f|
          # Write the message to the file
          first = true
          message.each { |line|
            if first
              first = false
              next if line =~ /From /
            end
            f << line
            f << "\n" unless line[-1] == ?\n
          }
          f.fsync if defined? f.fsync
        }
        File.link(tmp_name, new_name)
      ensure
        begin
          File.delete(tmp_name)
        rescue Errno::ENOENT
        end
      end
      new_name
    end
rubyfilter-0.12.orig/doc/classes/RFilter/Deliver.html0000444001013300101330000001534107773224251024222 0ustar yaegashiyaegashi00000000000000 Module: RFilter::Deliver
Module RFilter::Deliver
In: lib/rfilter/deliver.rb

This is a module containing methods that know how deliver to various kinds of message folder types.

Methods
Classes and Modules
Public Instance methods
deliver_mbox(filename, message)

Deliver message to an mbox filename.

The each method on message is used to get each line of the message. If the first line of the message is not an mbox From_ header, a fake one will be generated.

The file named by filename is opened for append, and flock locking is used to prevent other processes from modifying the file during delivery. No ".lock" style locking is performed. If that is desired, it should be performed before calling this method.

Returns the name of the file delivered to, or raises an exception if delivery failed.

write_to_mbox(output_io, message)

Write to an already opened mbox file. This low level function just takes care of escaping From_ lines in the message. See deliver_mbox for a more robust version.

deliver_pipe(command, message)

Deliver message to a pipe.

The supplied command is run in a sub process, and message.each is used to get each line of the message and write it to the pipe.

This method captures the Errno::EPIPE and ignores it, since this exception can be generated when the command exits before the entire message is written to it (which may or may not be an error).

The caller can (and should!) examine $? to see the exit status of the pipe command.

deliver_filter(message, *command) {|from_r| ...}

Deliver message to a filter and provide the io stream for reading the filtered content to the supplied block.

The supplied command is run in a sub process, and message.each is used to get each line of the message and write it to the filter.

The block passed to the function is run with IO objects for the stdout of the child process.

Returns the exit status of the child process.

deliver_maildir(dir, message)

Delivery message to a Maildir.

See cr.yp.to/proto/maildir.html for a description of the maildir mailbox format. Its primary advantage is that it requires no locks — delivery and access to the mailbox can occur at the same time.

The each method on message is used to get each line of the message. If the first line of the message is an mbox From_ line, it is discarded.

The filename of the successfully delivered message is returned. Will raise exceptions on any kind of error.

This method will attempt to create the Maildir if it does not exist.

rubyfilter-0.12.orig/doc/classes/RFilter/Deliver/0000755001013300101330000000000007773224251023332 5ustar yaegashiyaegashi00000000000000rubyfilter-0.12.orig/doc/classes/RFilter/Deliver/LockingError.html0000444001013300101330000000274007773224251026621 0ustar yaegashiyaegashi00000000000000 Class: RFilter::Deliver::LockingError
Class RFilter::Deliver::LockingError
In: lib/rfilter/deliver.rb
Parent: DeliveryError
rubyfilter-0.12.orig/doc/classes/RFilter/Deliver/DeliveryError.html0000444001013300101330000000264107773224251027016 0ustar yaegashiyaegashi00000000000000 Class: RFilter::Deliver::DeliveryError
Class RFilter::Deliver::DeliveryError
In: lib/rfilter/deliver.rb
Parent: StandardError
rubyfilter-0.12.orig/doc/classes/RFilter/Deliver/NotAMailbox.html0000444001013300101330000000273607773224251026403 0ustar yaegashiyaegashi00000000000000 Class: RFilter::Deliver::NotAMailbox
Class RFilter::Deliver::NotAMailbox
In: lib/rfilter/deliver.rb
Parent: DeliveryError
rubyfilter-0.12.orig/doc/classes/RFilter/Deliver/NotAFile.html0000444001013300101330000000273007773224251025661 0ustar yaegashiyaegashi00000000000000 Class: RFilter::Deliver::NotAFile
Class RFilter::Deliver::NotAFile
In: lib/rfilter/deliver.rb
Parent: DeliveryError
rubyfilter-0.12.orig/doc/classes/RFilter/AddressTagger.src/0000755001013300101330000000000007773224251025245 5ustar yaegashiyaegashi00000000000000rubyfilter-0.12.orig/doc/classes/RFilter/AddressTagger.src/M000050.html0000444001013300101330000000110207773224251026764 0ustar yaegashiyaegashi00000000000000 new (RFilter::AddressTagger)
# File lib/rfilter/tagged.rb, line 40
    def initialize(key, delimiter, strength)
      @key = key
      @delimiter = delimiter
      @strength = strength
    end
rubyfilter-0.12.orig/doc/classes/RFilter/AddressTagger.src/M000051.html0000444001013300101330000000115207773224251026772 0ustar yaegashiyaegashi00000000000000 dated (RFilter::AddressTagger)
# File lib/rfilter/tagged.rb, line 48
    def dated(address, expires)
      tag_address(address, expires.strftime("%Y%m%d%H%S"), 'd')
    end
rubyfilter-0.12.orig/doc/classes/RFilter/AddressTagger.src/M000052.html0000444001013300101330000000120307773224251026770 0ustar yaegashiyaegashi00000000000000 keyword (RFilter::AddressTagger)
# File lib/rfilter/tagged.rb, line 52
    def keyword(address, keyword)
      tag_address(address, keyword.downcase.gsub(/[^\w\d]/, '_'), 'k')
    end
rubyfilter-0.12.orig/doc/classes/RFilter/AddressTagger.src/M000053.html0000444001013300101330000000127307773224251027000 0ustar yaegashiyaegashi00000000000000 verify (RFilter::AddressTagger)
# File lib/rfilter/tagged.rb, line 59
    def verify(address)
      text, type, hmac = tag_parts(address)
      raise ArgumentError, "address not tagged" unless hmac
      hmac_digest(text, hmac.length / 2) == hmac
    end
rubyfilter-0.12.orig/doc/classes/RFilter/AddressTagger.html0000444001013300101330000001031707773224251025345 0ustar yaegashiyaegashi00000000000000 Class: RFilter::AddressTagger
Class RFilter::AddressTagger
In: lib/rfilter/tagged.rb
Parent: Object
Methods
dated    keyword    new    verify   
Attributes
delimiter  [RW] 
key  [RW] 
strength  [RW] 
Public Class methods
new(key, delimiter, strength)
Public Instance methods
dated(address, expires)

expires is the absolute time this dated address will expire. E.g. Time.now + (60 * 60 * 24 * age)

keyword(address, keyword)
verify(address)

Returns true if an address verifies. I.e. that the text portion of the tag matches its HMAC. Throws an ArgumentError if the address isn’t tagged at all.

rubyfilter-0.12.orig/doc/classes/RFilter/MTA.html0000444001013300101330000000300707773224251023245 0ustar yaegashiyaegashi00000000000000 Module: RFilter::MTA
Module RFilter::MTA
In: lib/rfilter/mta.rb

RFilter::MTA currently holds the EX_ constants from sysexits.h as well as a few EXITCODE_ constants that can be used when returning an error to an SMTP delivery agent (e.g. through a .forward script).

rubyfilter-0.12.orig/doc/classes/RFilter/KeyedMailbox.src/0000755001013300101330000000000007773224251025103 5ustar yaegashiyaegashi00000000000000rubyfilter-0.12.orig/doc/classes/RFilter/KeyedMailbox.src/M000021.html0000444001013300101330000000101407773224251026622 0ustar yaegashiyaegashi00000000000000 new (RFilter::KeyedMailbox)
# File lib/rfilter/keyed_mailbox.rb, line 54
    def initialize(path)
      @path = path.to_str.freeze
    end
rubyfilter-0.12.orig/doc/classes/RFilter/KeyedMailbox.src/M000022.html0000444001013300101330000000104207773224251026624 0ustar yaegashiyaegashi00000000000000 save (RFilter::KeyedMailbox)
# File lib/rfilter/keyed_mailbox.rb, line 62
    def save(message)
      save_key(message, deliver_maildir(@path, message))
    end
rubyfilter-0.12.orig/doc/classes/RFilter/KeyedMailbox.src/M000023.html0000444001013300101330000000130107773224251026623 0ustar yaegashiyaegashi00000000000000 retrieve (RFilter::KeyedMailbox)
# File lib/rfilter/keyed_mailbox.rb, line 68
    def retrieve(key)
      begin
        message_filename(dereference_key(key)).untaint
      rescue Errno::ENOENT
        nil
      end
    end
rubyfilter-0.12.orig/doc/classes/RFilter/KeyedMailbox.src/M000024.html0000444001013300101330000000132207773224251026627 0ustar yaegashiyaegashi00000000000000 delete (RFilter::KeyedMailbox)
# File lib/rfilter/keyed_mailbox.rb, line 77
    def delete(key)
      begin
        File.delete(*[key_filename(key),
                      message_filename(dereference_key(key))].compact)
      rescue Errno::ENOENT
      end
    end
rubyfilter-0.12.orig/doc/classes/RFilter/KeyedMailbox.src/M000025.html0000444001013300101330000000167207773224251026640 0ustar yaegashiyaegashi00000000000000 expire (RFilter::KeyedMailbox)
# File lib/rfilter/keyed_mailbox.rb, line 87
    def expire(age)
      cutoff = Time.now - (60 * 60 * 24) * age
      [ File.join(@path, 'new', '*'),
        File.join(@path, 'cur', '*'),
        File.join(@path, '.index', '*')].each { |glob|
        Dir[glob].each { |file|
          File::delete(file) if File::mtime(file) <= cutoff
        }
      }
    end
rubyfilter-0.12.orig/doc/classes/RFilter/KeyedMailbox.src/M000026.html0000444001013300101330000000141207773224251026631 0ustar yaegashiyaegashi00000000000000 each_key (RFilter::KeyedMailbox)
# File lib/rfilter/keyed_mailbox.rb, line 100
    def each_key
      Dir[File.join(@path, '.index', '*')].each { |file_name|
        key = File.basename(file_name)
        if valid_key(key)
          yield key
        end
      }
    end
rubyfilter-0.12.orig/doc/classes/RFilter/KeyedMailbox.src/M000027.html0000444001013300101330000000146107773224251026636 0ustar yaegashiyaegashi00000000000000 key_mtime (RFilter::KeyedMailbox)
# File lib/rfilter/keyed_mailbox.rb, line 111
    def key_mtime(key)
      raise ArgumentError, "expected a valid key" unless valid_key(key)
      File::mtime(File.join(@path, '.index', key))
    rescue Errno::ENOENT
      return nil
    end
rubyfilter-0.12.orig/doc/classes/RFilter/KeyedMailbox.src/M000028.html0000444001013300101330000000101407773224251026631 0ustar yaegashiyaegashi00000000000000 path (RFilter::KeyedMailbox)
# File lib/rfilter/keyed_mailbox.rb, line 120
    def path
      return @path
    end
rubyfilter-0.12.orig/doc/classes/RFilter/KeyedMailbox.html0000444001013300101330000001534607773224251025212 0ustar yaegashiyaegashi00000000000000 Class: RFilter::KeyedMailbox
Class RFilter::KeyedMailbox
In: lib/rfilter/keyed_mailbox.rb
Parent: Object

A KeyedMailbox object implements a message mailbox indexed by a unique key string for each message. When a message is saved into the store, the message’s key is returned. Later, the key is used to retrieve file name the message is stored in.

The message store has the following characteristics:

  1. It is a Maildir, so various mail programs can read the messages directly and without adversely affecting the mailbox.
  2. The key is very hard to guess.
  3. The key is short and can be included in a message subject or in the extension of a return address (suitable for mailing list style confirmations).
Methods
delete    each_key    expire    key_mtime    new    path    retrieve    save   
Included modules
Public Class methods
new(path)

Creates a confirmation queue object that manages a confirmation queue in directory path.

Public Instance methods
save(message)

Saves a message into a confirmation queue and returns a string key that can be used to retrieve it later. They key is a string of hex digits and dashes, suitable for inclusion in a message subject or "VERP" return address.

retrieve(key)

Get the file name holding the message associated with key, or returns nil if the message is missing.

delete(key)

Given a key, delete the message associated with it.

expire(age)

Expire messages in the confirmation queue older than age days old.

each_key() {|key| ...}

Yield each key in the mailbox along with its most recent modification time.

key_mtime(key)

Yield the time a given key was last modified as a Time object or nil if the key doesn’t exist.

path()

Return the path of this keyed mailbox (same value passed in #new).

rubyfilter-0.12.orig/doc/classes/RFilter/DeliveryAgent.html0000444001013300101330000003557607773224251025406 0ustar yaegashiyaegashi00000000000000 Class: RFilter::DeliveryAgent
Class RFilter::DeliveryAgent
In: lib/rfilter/delivery_agent.rb
Parent: Object

The RFilter::DeliveryAgent class allows flexible delivery of a mail message to a mailbox. It is designed to make mail filtering scripts easy to write, by allowing the filter author to concentrate on the filter logic and not the particulars of the message or folder format.

It is designed primarily to work as an DeliveryAgent (local delivery agent) for an SMTP server. It should work well as the basis for a script run from a .forward or .qmail file.

Methods
body    defer    exitcode    filter    header    log    logging_level    logging_level=    message    message=    new    pipe    process    reject    save   
Classes and Modules
Included modules
Public Class methods
new(input, logfile)

Create a new RFilter::DeliveryAgent object.

input may be a RMail::Message object (in which case, it is used directly). Otherwise, it is passed to RMail::Message.new and used to create a new RMail::Message object.

log may be nil (to disable logging completely) or a file name to which log messages will be appended.

process(input, logfile) {|lda| ...}

Takes the same input as #new, but passes the created RFilter::DeliveryAgent to the supplied block. The idea is that the entire delivery script is contained within the block.

This function tries to log exceptions that aren’t DeliveryComplete exceptions to the lda’s log. If it can log them, it defers the delivery. But if it can’t, it re-raises the exception so the caller can more properly deal with the exception.

Expected use:

 begin
   RFilter::DeliveryAgent.process(stdin, "my-log-file") { |lda|
     # ...code uses lda to deliver mail...
   }
 rescue RFilter::DeliveryAgent::DeliveryComplete => exception
   exit(RFilter::DeliveryAgent.exitcode(exception))
 rescue Exception
   ... perhaps log the exception to a hard coded file ...
   exit(RFilter::MTA::EX_TEMPFAIL)
 end
exitcode(exception)

This function expects the exception argument to be a RFilter::DeliveryAgent::DeliveryComplete subclass. The function will return the appropriate exitcode that the process should exit with.

Public Instance methods
save(folder, continue = false)

Save this message to mail folder. folder must be the file name of the mailbox. If folder ends in a slash (/) then the mailbox will be considered to be in Maildir format, otherwise it will be a Unix mbox folder.

If continue is false (the default), a RFilter::DeliveryAgent::DeliverySuccess exception is raised upon successful delivery. Otherwise, the method simply returns upon successful delivery.

Upon failure, the function raises an exception as determined by RFilter::Deliver.deliver_mbox or RFilter::Deliver.deliver_maildir.

See also: RFilter::Deliver.deliver_mbox and RFilter::Deliver.deliver_maildir.

reject(reason = nil)

Reject this message. Logs the reason for the rejection and raises a RFilter::DeliveryAgent::DeliveryReject exception.

defer(reason = nil)

Reject this message for now, but request that it be queued for re-delivery in the future. Logs the reason for the rejection and raises a RFilter::DeliveryAgent::DeliveryDefer exception.

pipe(command, continue = false)

Pipe this message to a command. command must be a string specifying a command to pipe the message to.

If continue is false, then a successful delivery to the pipe will raise a RFilter::DeliveryAgent::DeliverySuccess exception. If continue is true, then a successful delivery will simply return. Regardless of continue, a failure to deliver to the pipe will raise a RFilter::DeliveryAgent::DeliveryCommandFailure exception.

See also: RFilter::Deliver.deliver_pipe.

filter(*command)

Filter this message through a command. command must be a string or an array of strings specifying a command to filter the message through (it is passed to the Kernel::exec method).

If the command does not exit with a status of 0, a RFilter::DeliveryAgent::DeliveryCommandFailure exception is raised and the current message is not replaced.

See also: RFilter::Deliver.deliver_filter.

log(level, str)

Log a string to the log. If the current log is nil or level is greater than the current logging level, then the string will not be logged.

See also #logging_level, #logging_level=

logging_level()

Return the current logging level.

See also: #logging_level=, #log

logging_level=(level)

Set the current logging level. The level must be a number no less than one.

See also: #logging_level, #log

message()

Return the RMail::Message object associated with this RFilter::DeliveryAgent.

See also: #header, #body

message=(message)

Sets the message (which should be a RMail::Message object) that we’re delivering.

header()

Return the header of the message as a RMail::Header object. This is short hand for lda.message.header.

See also: #message, #body

body()

Return the body of the message as an array of strings. This is short hand for lda.message.body.

See also: #message, #header

rubyfilter-0.12.orig/doc/classes/RFilter/DeliveryAgent/0000755001013300101330000000000007773224251024502 5ustar yaegashiyaegashi00000000000000rubyfilter-0.12.orig/doc/classes/RFilter/DeliveryAgent/DeliverySuccess.src/0000755001013300101330000000000007773224251030404 5ustar yaegashiyaegashi00000000000000rubyfilter-0.12.orig/doc/classes/RFilter/DeliveryAgent/DeliverySuccess.src/M000048.html0000444001013300101330000000105607773224251032142 0ustar yaegashiyaegashi00000000000000 new (RFilter::DeliveryAgent::DeliverySuccess)
# File lib/rfilter/delivery_agent.rb, line 82
      def initialize(message)
        super
      end
rubyfilter-0.12.orig/doc/classes/RFilter/DeliveryAgent/DeliveryReject.html0000444001013300101330000000432207773224251030307 0ustar yaegashiyaegashi00000000000000 Class: RFilter::DeliveryAgent::DeliveryReject
Class RFilter::DeliveryAgent::DeliveryReject
In: lib/rfilter/delivery_agent.rb
Parent: DeliveryComplete

Raised by RFilter::DeliveryAgent#reject.

Methods
new   
Public Class methods
new(message)
rubyfilter-0.12.orig/doc/classes/RFilter/DeliveryAgent/DeliveryComplete.src/0000755001013300101330000000000007773224251030544 5ustar yaegashiyaegashi00000000000000rubyfilter-0.12.orig/doc/classes/RFilter/DeliveryAgent/DeliveryComplete.src/M000047.html0000444001013300101330000000105707773224251032302 0ustar yaegashiyaegashi00000000000000 new (RFilter::DeliveryAgent::DeliveryComplete)
# File lib/rfilter/delivery_agent.rb, line 55
      def initialize(message)
        super
      end
rubyfilter-0.12.orig/doc/classes/RFilter/DeliveryAgent/DeliveryComplete.html0000444001013300101330000000507707773224251030653 0ustar yaegashiyaegashi00000000000000 Class: RFilter::DeliveryAgent::DeliveryComplete
Class RFilter::DeliveryAgent::DeliveryComplete
In: lib/rfilter/delivery_agent.rb
Parent: StandardError

A DeliveryComplete exception is one that indicates that RFilter::DeliveryAgent’s delivery process is now complete and. The DeliveryComplete exception is never thrown itself, but its various subclasses are.

Methods
new   
Public Class methods
new(message)

Create a new DeliveryComplete exception with a given message.

rubyfilter-0.12.orig/doc/classes/RFilter/DeliveryAgent/DeliveryReject.src/0000755001013300101330000000000007773224251030210 5ustar yaegashiyaegashi00000000000000rubyfilter-0.12.orig/doc/classes/RFilter/DeliveryAgent/DeliveryReject.src/M000045.html0000444001013300101330000000105507773224251031742 0ustar yaegashiyaegashi00000000000000 new (RFilter::DeliveryAgent::DeliveryReject)
# File lib/rfilter/delivery_agent.rb, line 89
      def initialize(message)
        super
      end
rubyfilter-0.12.orig/doc/classes/RFilter/DeliveryAgent/DeliverySuccess.html0000444001013300101330000000452007773224251030503 0ustar yaegashiyaegashi00000000000000 Class: RFilter::DeliveryAgent::DeliverySuccess
Class RFilter::DeliveryAgent::DeliverySuccess
In: lib/rfilter/delivery_agent.rb
Parent: DeliveryComplete

Raised upon delivery success, unless the continue flag of the RFilter::DeliveryAgent delivery method was set to true.

Methods
new   
Public Class methods
new(message)
rubyfilter-0.12.orig/doc/classes/RFilter/DeliveryAgent/DeliveryDefer.src/0000755001013300101330000000000007773224251030021 5ustar yaegashiyaegashi00000000000000rubyfilter-0.12.orig/doc/classes/RFilter/DeliveryAgent/DeliveryDefer.src/M000044.html0000444001013300101330000000105407773224251031551 0ustar yaegashiyaegashi00000000000000 new (RFilter::DeliveryAgent::DeliveryDefer)
# File lib/rfilter/delivery_agent.rb, line 96
      def initialize(message)
        super
      end
rubyfilter-0.12.orig/doc/classes/RFilter/DeliveryAgent/DeliveryDefer.html0000444001013300101330000000431507773224251030122 0ustar yaegashiyaegashi00000000000000 Class: RFilter::DeliveryAgent::DeliveryDefer
Class RFilter::DeliveryAgent::DeliveryDefer
In: lib/rfilter/delivery_agent.rb
Parent: DeliveryComplete

Raised by RFilter::DeliveryAgent#defer.

Methods
new   
Public Class methods
new(message)
rubyfilter-0.12.orig/doc/classes/RFilter/DeliveryAgent/LoggingError.html0000444001013300101330000000501007773224251027762 0ustar yaegashiyaegashi00000000000000 Class: RFilter::DeliveryAgent::LoggingError
Class RFilter::DeliveryAgent::LoggingError
In: lib/rfilter/delivery_agent.rb
Parent: StandardError

This exception is raised when there is a problem logging.

Methods
new   
Attributes
original_exception  [R] 
Public Class methods
new(message, original_exception = nil)
rubyfilter-0.12.orig/doc/classes/RFilter/DeliveryAgent/DeliveryCommandFailure.html0000444001013300101330000000534407773224251031766 0ustar yaegashiyaegashi00000000000000 Class: RFilter::DeliveryAgent::DeliveryCommandFailure
Class RFilter::DeliveryAgent::DeliveryCommandFailure
In: lib/rfilter/delivery_agent.rb
Parent: DeliveryComplete

Raised when the command run by #pipe or #filter fails.

Methods
new   
Attributes
status  [R] 

This is the exit status of the pipe command.

Public Class methods
new(message, status)
rubyfilter-0.12.orig/doc/classes/RFilter/DeliveryAgent/LoggingError.src/0000755001013300101330000000000007773224251027670 5ustar yaegashiyaegashi00000000000000rubyfilter-0.12.orig/doc/classes/RFilter/DeliveryAgent/LoggingError.src/M000046.html0000444001013300101330000000122707773224251031424 0ustar yaegashiyaegashi00000000000000 new (RFilter::DeliveryAgent::LoggingError)
# File lib/rfilter/delivery_agent.rb, line 63
      def initialize(message, original_exception = nil)
        super(message)
        @original_exception = original_exception
      end
rubyfilter-0.12.orig/doc/classes/RFilter/DeliveryAgent/DeliveryCommandFailure.src/0000755001013300101330000000000007773224251031662 5ustar yaegashiyaegashi00000000000000rubyfilter-0.12.orig/doc/classes/RFilter/DeliveryAgent/DeliveryCommandFailure.src/M000049.html0000444001013300101330000000113707773224251033421 0ustar yaegashiyaegashi00000000000000 new (RFilter::DeliveryAgent::DeliveryCommandFailure)
# File lib/rfilter/delivery_agent.rb, line 73
      def initialize(message, status)
        super(message)
        @status = status
      end
rubyfilter-0.12.orig/doc/classes/Deliver.src/0000755001013300101330000000000007773224251022551 5ustar yaegashiyaegashi00000000000000rubyfilter-0.12.orig/doc/classes/Deliver.src/M000012.html0000444001013300101330000000075107773224251024277 0ustar yaegashiyaegashi00000000000000 agent (Deliver)
# File bin/experimental-filter.rb, line 114
  def agent
    @__MAIL_DELIVERY_AGENT__
  end
rubyfilter-0.12.orig/doc/classes/Deliver.src/M000013.html0000444001013300101330000000105007773224251024271 0ustar yaegashiyaegashi00000000000000 main (Deliver)
# File bin/experimental-filter.rb, line 121
  def main
    agent.defer('no deliver method specified in configuration file')
  end
rubyfilter-0.12.orig/doc/classes/Deliver.src/M000014.html0000444001013300101330000000075207773224251024302 0ustar yaegashiyaegashi00000000000000 agent (Deliver)
# File bin/rdeliver.rb, line 176
      def agent
        @__MAIL_DELIVERY_AGENT__
      end
rubyfilter-0.12.orig/doc/classes/Deliver.src/M000015.html0000444001013300101330000000105107773224251024274 0ustar yaegashiyaegashi00000000000000 main (Deliver)
# File bin/rdeliver.rb, line 184
      def main
        agent.defer('no deliver method specified in configuration file')
      end
rubyfilter-0.12.orig/doc/classes/Deliver.html0000444001013300101330000001134707773224251022655 0ustar yaegashiyaegashi00000000000000 Class: Deliver
Class Deliver
In: bin/experimental-filter.rb
bin/rdeliver.rb
Parent: Object

The Deliver class used by the bin/rdeliver.rb script to provide a place for the user’s delivery script to run. The user’s delivery script executes in the context of this class, so methods defined with def do not pollute the global namespace. The user is expected to define a #main method that will be called to deliver the mail.

See also bin/rdeliver.rb

Methods
agent    agent    main    main   
Public Instance methods
agent()

Return the RFilter::DeliveryAgent object for the current message. This is all you need to retrieve, modify, and deliver the message.

main()

This method is called by bin/filter.rb after the Deliver object is instantiated. A default implementation that merely defers the delivery is provided, but the user is expected to replace this with a version that delivers the mail to the proper location.

agent()

Return the RFilter::DeliveryAgent object for the current message. This is all you need to retrieve, modify, and deliver the message.

main()

This method is called by bin/rdeliver.rb after the Deliver object is instantiated. A default implementation that merely defers the delivery is provided, but the user is expected to replace this with a version that delivers the mail to the proper location.

rubyfilter-0.12.orig/doc/classes/RFilter.html0000444001013300101330000001173607773224251022634 0ustar yaegashiyaegashi00000000000000 Module: RFilter
Module RFilter
In: lib/rfilter/keyed_mailbox.rb
lib/rfilter/delivery_agent.rb
lib/rfilter/tagged.rb
lib/rfilter/mta.rb
lib/rfilter/deliver.rb
  Copyright (C) 2001, 2002, 2003 Matt Armstrong.  All rights reserved.

Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

  1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
  2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
  3. The name of the author may not be used to endorse or promote products derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS’’ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

Classes and Modules
rubyfilter-0.12.orig/lib/0000755001013300101330000000000007773224251016735 5ustar yaegashiyaegashi00000000000000rubyfilter-0.12.orig/lib/rfilter/0000755001013300101330000000000007773224251020404 5ustar yaegashiyaegashi00000000000000rubyfilter-0.12.orig/lib/rfilter/deliver.rb0000444001013300101330000002226607773224251022371 0ustar yaegashiyaegashi00000000000000# # Copyright (C) 2001, 2002, 2003 Matt Armstrong. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # 3. The name of the author may not be used to endorse or promote # products derived from this software without specific prior # written permission. # # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE # GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER # IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR # OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN # IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # module RFilter # This is a module containing methods that know how deliver to # various kinds of message folder types. module Deliver @@mail_deliver_maildir_count = 0 SYNC_IF_NO_FSYNC = RUBY_VERSION >= "1.7" ? 0 : File::SYNC class DeliveryError < StandardError end class NotAFile < DeliveryError end class NotAMailbox < DeliveryError end class LockingError < DeliveryError end # Deliver +message+ to an mbox +filename+. # # The +each+ method on +message+ is used to get each line of the # message. If the first line of the message is not an mbox # From_ header, a fake one will be generated. # # The file named by +filename+ is opened for append, and +flock+ # locking is used to prevent other processes from modifying the # file during delivery. No ".lock" style locking is performed. # If that is desired, it should be performed before calling this # method. # # Returns the name of the file delivered to, or raises an # exception if delivery failed. def deliver_mbox(filename, message) return filename if filename == '/dev/null' File.open(filename, File::APPEND|File::WRONLY|File::CREAT|SYNC_IF_NO_FSYNC, 0600) { |f| max = 5 max.times { |i| break if f.flock(File::LOCK_EX | File::LOCK_NB) raise LockingError, "Timeout locking mailbox." if i == max - 1 sleep(1) } st = f.lstat unless st.file? raise NotAFile, "Can not deliver to #{filename}, not a regular file." end begin # Ignore SIGXFSZ, since we want to get the Errno::EFBIG # exception when the file is too big. old_handler = trap('XFSZ', 'IGNORE') || 'DEFAULT' write_to_mbox(f, message) begin f.fsync rescue NameError # NameError happens with older versions of Ruby that have # no File#fsync f.flush end rescue Exception => e begin begin f.flush rescue Exception end f.truncate(st.size) ensure raise e end ensure if old_handler trap('XFSZ', old_handler) end end f.flock(File::LOCK_UN) } filename end module_function :deliver_mbox # Write to an already opened mbox file. This low level function # just takes care of escaping From_ lines in the message. See # deliver_mbox for a more robust version. def write_to_mbox(output_io, message) first = true message.each { |line| if first first = false if line !~ /^From .*\d$/ from = "From foo@bar " + Time.now.asctime + "\n" output_io << from end elsif line =~ /^From / output_io << '>' end output_io << line output_io << "\n" unless line[-1] == ?\n } output_io << "\n" end module_function :write_to_mbox # Deliver +message+ to a pipe. # # The supplied +command+ is run in a sub process, and # message.each is used to get each line of the message # and write it to the pipe. # # This method captures the Errno::EPIPE and ignores it, # since this exception can be generated when the command exits # before the entire message is written to it (which may or may not # be an error). # # The caller can (and should!) examine $? to see the exit # status of the pipe command. def deliver_pipe(command, message) begin IO.popen(command, "w") { |io| message.each { |line| io << line io << "\n" unless line[-1] == ?\n } } rescue Errno::EPIPE # Just ignore. end end module_function :deliver_pipe # Deliver +message+ to a filter and provide the io stream for # reading the filtered content to the supplied block. # # The supplied +command+ is run in a sub process, and # message.each is used to get each line of the message # and write it to the filter. # # The block passed to the function is run with IO objects for the # stdout of the child process. # # Returns the exit status of the child process. def deliver_filter(message, *command) begin to_r, to_w = IO.pipe from_r, from_w = IO.pipe if pid = fork # parent to_r.close from_w.close writer = Thread::new { message.each { |line| to_w << line to_w << "\n" unless line[-1] == ?\n } to_w.close } yield from_r else # child begin to_w.close from_r.close STDIN.reopen(to_r) to_r.close STDOUT.reopen(from_w) from_w.close exec(*command) ensure exit! end end ensure writer.kill if writer and writer.alive? [ to_r, to_w, from_r, from_w ].each { |io| if io && !io.closed? begin io.close rescue Errno::EPIPE end end } end Process.waitpid2(pid, 0)[1] end module_function :deliver_filter # Delivery +message+ to a Maildir. # # See http://cr.yp.to/proto/maildir.html for a description of the # maildir mailbox format. Its primary advantage is that it # requires no locks -- delivery and access to the mailbox can # occur at the same time. # # The +each+ method on +message+ is used to get each line of the # message. If the first line of the message is an mbox # From_ line, it is discarded. # # The filename of the successfully delivered message is returned. # Will raise exceptions on any kind of error. # # This method will attempt to create the Maildir if it does not # exist. def deliver_maildir(dir, message) require 'socket' # First, make the required directories new = File.join(dir, 'new') tmp = File.join(dir, 'tmp') [ dir, new, tmp, File.join(dir, 'cur') ].each { |d| begin Dir.mkdir(d, 0700) rescue Errno::EEXIST raise unless FileTest::directory?(d) end } sequence = @@mail_deliver_maildir_count @@mail_deliver_maildir_count = @@mail_deliver_maildir_count.next tmp_name = nil new_name = nil hostname = Socket::gethostname.gsub(/[^\w]/, '_').untaint pid = Process::pid 3.times { |i| now = Time::now name = sprintf("%d.M%XP%dQ%d.%s", Time::now.tv_sec, Time::now.tv_usec, pid, sequence, hostname) tmp_name = File.join(tmp, name) new_name = File.join(new, name) begin File::stat(tmp_name) rescue Errno::ENOENT break rescue Exception raise if i == 2 end raise "Too many tmp file conflicts." if i == 2 sleep(2) } begin File.open(tmp_name, File::CREAT|File::EXCL|File::WRONLY|SYNC_IF_NO_FSYNC, 0600) { |f| # Write the message to the file first = true message.each { |line| if first first = false next if line =~ /From / end f << line f << "\n" unless line[-1] == ?\n } f.fsync if defined? f.fsync } File.link(tmp_name, new_name) ensure begin File.delete(tmp_name) rescue Errno::ENOENT end end new_name end module_function :deliver_maildir end end rubyfilter-0.12.orig/lib/rfilter/mta.rb0000444001013300101330000000473007773224251021514 0ustar yaegashiyaegashi00000000000000# # Copyright (C) 2001 Matt Armstrong. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # 3. The name of the author may not be used to endorse or promote # products derived from this software without specific prior # written permission. # # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE # GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER # IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR # OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN # IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # module RFilter # RFilter::MTA currently holds the EX_ constants from # sysexits.h as well as a few EXITCODE_ constants that can be used # when returning an error to an SMTP delivery agent (e.g. through a # .forward script). module MTA EX_USAGE = 64 # command line usage error EX_DATAERR = 65 # data format error EX_NOINPUT = 66 # cannot open input EX_NOUSER = 67 # addressee unknown EX_NOHOST = 68 # hostname unknown EX_UNAVAILABLE = 69 # service unavailable EX_SOFTWARE = 70 # internal software error EX_OSERR = 71 # system error (e.g., can't fork) EX_OSFILE = 72 # critical OS file missing EX_CANTCREAT = 73 # can't create (user) output file EX_IOERR = 74 # input/output error EX_TEMPFAIL = 75 # temp failure; user is invited to retry EX_PROTOCOL = 76 # remote error in protocol EX_NOPERM = 77 # permission denied EX_CONFIG = 78 # configuration DEFER EXITCODE_DEFER = EX_TEMPFAIL EXITCODE_REJECT = EX_NOPERM EXITCODE_DELIVERED = 0 end end rubyfilter-0.12.orig/lib/rfilter/tagged.rb0000444001013300101330000000566107773224251022172 0ustar yaegashiyaegashi00000000000000# # Copyright (C) 2002 Matt Armstrong. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # 3. The name of the author may not be used to endorse or promote # products derived from this software without specific prior # written permission. # # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE # GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER # IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR # OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN # IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # require 'mail/address' require 'hmac-sha1' module RFilter class AddressTagger attr :key, true attr :delimiter, true attr :strength, true def initialize(key, delimiter, strength) @key = key @delimiter = delimiter @strength = strength end # expires is the absolute time this dated address will expire. # E.g. Time.now + (60 * 60 * 24 * age) def dated(address, expires) tag_address(address, expires.strftime("%Y%m%d%H%S"), 'd') end def keyword(address, keyword) tag_address(address, keyword.downcase.gsub(/[^\w\d]/, '_'), 'k') end # Returns true if an address verifies. I.e. that the text portion # of the tag matches its HMAC. Throws an ArgumentError if the # address isn't tagged at all. def verify(address) text, type, hmac = tag_parts(address) raise ArgumentError, "address not tagged" unless hmac hmac_digest(text, hmac.length / 2) == hmac end private def tag_address(address, text, type) address = address.dup cookie = hmac_digest(text, @strength) address.local = format("%s%s%s.%s.%s", address.local, delimiter, text, type, cookie) address end def hmac_digest(text, strength) HMAC::SHA1.digest(@key, text)[0...strength].unpack("H*")[0] end def tag_parts(address) d = Regexp.quote(@delimiter) if address.local =~ /#{d}([\w\d]+)\.([\w]+)\.([0-9a-h]+)$/i [$1, $2, $3] end end end end rubyfilter-0.12.orig/lib/rfilter/delivery_agent.rb0000444001013300101330000002741607773224251023742 0ustar yaegashiyaegashi00000000000000# # Copyright (C) 2001, 2002, 2003 Matt Armstrong. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # 3. The name of the author may not be used to endorse or promote # products derived from this software without specific prior # written permission. # # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE # GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER # IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR # OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN # IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # require 'rmail/message' require 'rmail/parser' require 'rfilter/deliver' require 'rfilter/mta' module RFilter # The RFilter::DeliveryAgent class allows flexible delivery of # a mail message to a mailbox. It is designed to make mail # filtering scripts easy to write, by allowing the filter author to # concentrate on the filter logic and not the particulars of the # message or folder format. # # It is designed primarily to work as an DeliveryAgent (local # delivery agent) for an SMTP server. It should work well as the # basis for a script run from a .forward or .qmail file. class DeliveryAgent include RFilter::Deliver # A DeliveryComplete exception is one that indicates that # RFilter::DeliveryAgent's delivery process is now complete and. The # DeliveryComplete exception is never thrown itself, but its # various subclasses are. class DeliveryComplete < StandardError # Create a new DeliveryComplete exception with a given +message+. def initialize(message) super end end # This exception is raised when there is a problem logging. class LoggingError < StandardError attr_reader :original_exception def initialize(message, original_exception = nil) super(message) @original_exception = original_exception end end # Raised when the command run by #pipe or #filter fails. class DeliveryCommandFailure < DeliveryComplete # This is the exit status of the pipe command. attr_reader :status def initialize(message, status) super(message) @status = status end end # Raised upon delivery success, unless the +continue+ flag of the # RFilter::DeliveryAgent delivery method was set to true. class DeliverySuccess < DeliveryComplete def initialize(message) super end end # Raised by RFilter::DeliveryAgent#reject. class DeliveryReject < DeliveryComplete def initialize(message) super end end # Raised by RFilter::DeliveryAgent#defer. class DeliveryDefer < DeliveryComplete def initialize(message) super end end # Create a new RFilter::DeliveryAgent object. # # +input+ may be a RMail::Message object (in which case, # it is used directly). Otherwise, it is passed to # RMail::Message.new and used to create a new # RMail::Message object. # # +log+ may be nil (to disable logging completely) or a file name # to which log messages will be appended. def initialize(input, logfile) @logfile = if logfile.nil? nil else File.open(logfile, File::CREAT|File::APPEND|File::WRONLY, 0600) end @message = if input.is_a?(RMail::Message) input else RMail::Parser.new.parse(input) end @logging_level = 2 end # Save this message to mail folder. +folder+ must be the file # name of the mailbox. If +folder+ ends in a slash (/) then the # mailbox will be considered to be in Maildir format, otherwise it # will be a Unix mbox folder. # # If +continue+ is false (the default), a # RFilter::DeliveryAgent::DeliverySuccess exception is raised upon # successful delivery. Otherwise, the method simply returns upon # successful delivery. # # Upon failure, the function raises an exception as determined by # RFilter::Deliver.deliver_mbox or RFilter::Deliver.deliver_maildir. # # See also: RFilter::Deliver.deliver_mbox and # RFilter::Deliver.deliver_maildir. def save(folder, continue = false) log(2, "Action: save to #{folder.inspect}") retval = if folder =~ %r{(.*[^/])/$} deliver_maildir($1, @message) else deliver_mbox(folder, @message) end raise DeliverySuccess, "saved to mbox #{folder.inspect}" unless continue retval end # Reject this message. Logs the +reason+ for the rejection and # raises a RFilter::DeliveryAgent::DeliveryReject exception. def reject(reason = nil) log(2, "Action: reject: " + reason.to_s) raise DeliveryReject.new(reason.to_s) end # Reject this message for now, but request that it be queued for # re-delivery in the future. Logs the +reason+ for the rejection # and raises a RFilter::DeliveryAgent::DeliveryDefer exception. def defer(reason = nil) log(2, "Action: defer: " + reason.to_s) raise DeliveryDefer.new(reason.to_s) end # Pipe this message to a command. +command+ must be a string # specifying a command to pipe the message to. # # If +continue+ is false, then a successful delivery to the pipe # will raise a RFilter::DeliveryAgent::DeliverySuccess exception. # If +continue+ is true, then a successful delivery will simply # return. Regardless of +continue+, a failure to deliver to the # pipe will raise a RFilter::DeliveryAgent::DeliveryCommandFailure # exception. # # See also: RFilter::Deliver.deliver_pipe. def pipe(command, continue = false) log(2, "Action: pipe to #{command.inspect}") deliver_pipe(command, @message) if $? != 0 m = "pipe failed for command #{command.inspect}" log(1, "Error: " + m) raise DeliveryCommandFailure.new(m, $?) end unless continue raise DeliverySuccess.new("pipe to #{command.inspect}") end end # Filter this message through a command. +command+ must be a # string or an array of strings specifying a command to filter the # message through (it is passed to the Kernel::exec method). # # If the command does not exit with a status of 0, a # RFilter::DeliveryAgent::DeliveryCommandFailure exception is # raised and the current message is not replaced. # # See also: RFilter::Deliver.deliver_filter. def filter(*command) log(2, "Action: filter through #{command.inspect}") msg = nil status = deliver_filter(@message, *command) { |io| msg = RMail::Parser.new.parse(io) } if status != 0 m = format("filter failed for command %s (status %s)", command.inspect, status.inspect) log(1, "Error: " + m) raise DeliveryCommandFailure.new(m, status) end @message = msg end # Log a string to the log. If the current log is nil or +level+ # is greater than the current logging level, then the string will # not be logged. # # See also #logging_level, #logging_level= def log(level, str) if level <= 0 and @logfile.nil? raise LoggingError, "failed to log high priority message: #{str}" end return if @logfile.nil? or level > @logging_level begin @logfile.flock(File::LOCK_EX) @logfile.print(Time.now.strftime("%Y/%m/%d %H:%M:%S ")) @logfile.print(sprintf("%05d: ", Process.pid)) @logfile.puts(str) @logfile.flush @logfile.flock(File::LOCK_UN) rescue # FIXME: this isn't tested raise LoggingError.new("failed to log message: #{str}", $!) end end # Return the current logging level. # # See also: #logging_level=, #log def logging_level @logging_level end # Set the current logging level. The +level+ must be a number no # less than one. # # See also: #logging_level, #log def logging_level=(level) level = Integer(level) raise ArgumentError, "invalid logging level value #{level}" if level < 1 @logging_level = level end # Return the RMail::Message object associated with this # RFilter::DeliveryAgent. # # See also: #header, #body def message @message end # Sets the message (which should be a RMail::Message object) that # we're delivering. def message=(message) @message = message end # Return the header of the message as a RMail::Header object. # This is short hand for lda.message.header. # # See also: #message, #body def header @message.header end # Return the body of the message as an array of strings. This is # short hand for lda.message.body. # # See also: #message, #header def body @message.body end class << self # Takes the same input as #new, but passes the created # RFilter::DeliveryAgent to the supplied block. The idea # is that the entire delivery script is contained within the # block. # # This function tries to log exceptions that aren't # DeliveryComplete exceptions to the lda's log. If it can log # them, it defers the delivery. But if it can't, it re-raises # the exception so the caller can more properly deal with the # exception. # # Expected use: # # begin # RFilter::DeliveryAgent.process(stdin, "my-log-file") { |lda| # # ...code uses lda to deliver mail... # } # rescue RFilter::DeliveryAgent::DeliveryComplete => exception # exit(RFilter::DeliveryAgent.exitcode(exception)) # rescue Exception # ... perhaps log the exception to a hard coded file ... # exit(RFilter::MTA::EX_TEMPFAIL) # end def process(input, logfile) begin lda = RFilter::DeliveryAgent.new(input, logfile) yield lda lda.defer("finished without a final delivery") rescue Exception => exception if exception.class <= DeliveryComplete raise exception else begin lda.log(0, "uncaught exception: " + exception.inspect) lda.log(0, "uncaught exception backtrace:\n " + exception.backtrace.join("\n ")) lda.defer("uncaught exception") rescue Exception if $!.class <= DeliveryComplete # The lda.defer above will generate this, just re-raise # the delivery status exception. raise else # Any errors logging in the uncaught exception and we # just re-raise the original exception raise exception end end end end end # This function expects the +exception+ argument to be a # RFilter::DeliveryAgent::DeliveryComplete subclass. The function # will return the appropriate exitcode that the process should # exit with. def exitcode(exception) case exception when DeliverySuccess RFilter::MTA::EXITCODE_DELIVERED when DeliveryReject RFilter::MTA::EXITCODE_REJECT when DeliveryComplete RFilter::MTA::EXITCODE_DEFER else raise ArgumentError, "argument is not a DeliveryComplete exception: " + "#{exception.inspect} (#{exception.class})" end end end end end rubyfilter-0.12.orig/lib/rfilter/keyed_mailbox.rb0000444001013300101330000001600707773224251023547 0ustar yaegashiyaegashi00000000000000#-- # Copyright (C) 2002, 2003 Matt Armstrong. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # 3. The name of the author may not be used to endorse or promote # products derived from this software without specific prior # written permission. # # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE # GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER # IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR # OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN # IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # require 'rfilter/deliver' require 'digest/md5' require 'timeout' module RFilter # A KeyedMailbox object implements a message mailbox indexed by a # unique key string for each message. When a message is saved into # the store, the message's key is returned. Later, the key is used # to retrieve file name the message is stored in. # # The message store has the following characteristics: # # 1. It is a Maildir, so various mail programs can read the messages # directly and without adversely affecting the mailbox. # 2. The key is very hard to guess. # 3. The key is short and can be included in a message subject or in # the extension of a return address (suitable for mailing list style # confirmations). class KeyedMailbox include RFilter::Deliver # Creates a confirmation queue object that manages a confirmation # queue in directory +path+. def initialize(path) @path = path.to_str.freeze end # Saves a message into a confirmation queue and returns a string # key that can be used to retrieve it later. They key is a string # of hex digits and dashes, suitable for inclusion in a message # subject or "VERP" return address. def save(message) save_key(message, deliver_maildir(@path, message)) end # Get the file name holding the message associated with +key+, or # returns nil if the message is missing. def retrieve(key) begin message_filename(dereference_key(key)).untaint rescue Errno::ENOENT nil end end # Given a key, delete the message associated with it. def delete(key) begin File.delete(*[key_filename(key), message_filename(dereference_key(key))].compact) rescue Errno::ENOENT end end # Expire messages in the confirmation queue older than +age+ days # old. def expire(age) cutoff = Time.now - (60 * 60 * 24) * age [ File.join(@path, 'new', '*'), File.join(@path, 'cur', '*'), File.join(@path, '.index', '*')].each { |glob| Dir[glob].each { |file| File::delete(file) if File::mtime(file) <= cutoff } } end # Yield each key in the mailbox along with its most recent # modification time. def each_key Dir[File.join(@path, '.index', '*')].each { |file_name| key = File.basename(file_name) if valid_key(key) yield key end } end # Yield the time a given key was last modified as a Time object or # nil if the key doesn't exist. def key_mtime(key) raise ArgumentError, "expected a valid key" unless valid_key(key) File::mtime(File.join(@path, '.index', key)) rescue Errno::ENOENT return nil end # Return the path of this keyed mailbox (same value passed in # #new). def path return @path end private def valid_key(key) (key =~ /^[A-Za-z\d]{10}$/) ? true : false end def key_filename(key) raise ArgumentError, key unless valid_key(key) File.join(@path, '.index', key.upcase).untaint end def dereference_key(key) IO.readlines(key_filename(key))[0] end def message_filename(basename) raise ArgumentError, basename unless basename =~ /^[\w\.]+$/ basename.untaint catch(:found) { [ File.join(@path, 'new', basename), File.join(@path, 'cur', basename) ].each { |curr| Dir["#{curr}*"].each { |fullname| throw(:found, fullname.untaint) } } nil } end def save_key(message, maildir_filename) # First make the required directories begin Dir.mkdir(File.join(@path, '.index')) rescue Errno::EEXIST raise unless FileTest::directory?(@path) end hash = nil tmp_dir = File.join(@path, 'tmp') index_dir = File.join(@path, '.index') try_count = 1 begin hash = hash_str(message) tmp_name = File.join(tmp_dir, hash) index_name = File.join(index_dir, hash) File.open(tmp_name, File::CREAT|File::EXCL|File::WRONLY|File::SYNC, 0600) { |f| f.print(File.basename(maildir_filename)) } File.link(tmp_name, index_name) rescue Errno::EEXIST raise if try_count >= 5 sleep(2) try_count = try_count.next retry ensure begin File.delete(tmp_name) unless tmp_name.nil? rescue Errno::ENOENT end end hash end def hash_str(message) # Hash the message, the current time, the current pid and # unambiguously separate them with the 0 byte. md5 = Digest::MD5::new md5.update(Marshal.dump(message)) md5.update(0.chr) md5.update(Time.now.to_s) md5.update(0.chr) md5.update(Process.pid.to_s) # And if we have the last hash we generated, update with the last # two bytes of it. last_hash_name = File.join(@path, '.last_hash') begin timeout(10) { File.open(last_hash_name, 'rb') { |f| f.flock(File::LOCK_SH) f.seek(14) if last_two = f.read(2) md5.update(0.chr) md5.update(last_two) end } } rescue Errno::ENOENT rescue TimeoutError end hash = md5.digest begin timeout(10) { File.open(last_hash_name, 'wb', 0600) { |f| f.flock(File::LOCK_EX) f.print(hash) } } rescue TimeoutError end hash[0..4].unpack("H*")[0].upcase end end end rubyfilter-0.12.orig/NEWS0000444001013300101330000000361507773224251016671 0ustar yaegashiyaegashi00000000000000= Changes in RubyFilter 0.12 (released 2003-12-26) - Ruby 1.8.1 compatability changes. = Changes in RubyFilter 0.11 (released 2003-09-17) - Ruby 1.8.0 compatability changes. - No longer verify that a unix mbox file is in the correct format before appending to it. = Changes in RubyFilter 0.10 (released 2003-02-04) - Add RFilter::Deliver.write_to_mbox that writes to an already open mailbox file. This can be used when none of the anal checks, locking, etc. that RFilter::Deliver.deliver_mbox does is desireable. - Add RFilter::KeyedMailbox#each_key and RFilter::KeyedMailbox#key_mtime methods that can be used to implement expiry when you want to process the message before it is deleted. - DeliveryAgent#deliver_mbox now ignores the SIGXFSZ exception and recovers from the EFBIG Errno exception. = Changes in RubyFilter 0.9 (released 2002-01-16) - New DeliveryAgent#filter method that can be used to filter a message through an external command, possibly changing the message. - DeliveryAgent::DeliveryPipeFailure renamed to DeliveryAgent::DeliveryCommandFailure since it is now raised by DeliveryAgent#pipe as well as DeliveryAgent#filter. - DeliveryAgent#defer and DeliveryAgent#reject now have a default argument for the message string. - Other changes to bring up to date with current RubyMail and Ruby 1.8.0. = Changes in RubyFilter 0.8 (released 2002-03-18) - Created from RubyMail 0.7 - All Mail::* constants moved into the RFilter:: module. - Mail::Deliver.deliver_mbox returns the name of the file delivered to. - rdeliver.rb now prints DeliveryDever and DeliveryReject messages to stdout in the hopes that the MTA will include them in the bounce message. - Renamed Mail::LDA to RFilter::DeliveryAgent. - RFilter::Deliver.deliver_maildir -- bring in line with the Maildir delivery spec (i.e. stat the tmp file before trying to open it instead of relying on O_EXCL). rubyfilter-0.12.orig/TODO0000444001013300101330000000541107773224251016656 0ustar yaegashiyaegashi00000000000000-*- outline -*- = Bug Fixes - Maildir delivery is unsafe. See new http://cr.yp.to/proto/maildir.html = Features - Mail::Deliver - Uniform lock handling in deliver_mbox and deliver_pipe - Mail::DeliveryAgent - Implement some kind of plugin architecture. It can be simple, based on requiring files and including them into the Deliver class. Or perhaps a Deliver::Plugin class to fix namespace issues. - filter method: make it work if the command starts printing before it has read the entire message. - forward method? - pipe method (lockfile attribute?) - ignore method (reason attribute?) - resend method (call it forward?) - Mail::DeliveryAgent#save doen't generate a DeliveryFailure for all possible delivery failures. Test this. - Unknown - Implement an auto-responder. - Implement sender based pending queues The queue lives under a single directory of this structure: /senders//msgs/ /senders//meta /sender-count /lock Access to the entire directory is controlled by an flock on the lock file. This simplifies other issues. The is a sanitized version of the actual sender address. Every character outside the range of a-zA-Z and @ is HEX encoded. - Content From: field set to the user that received the mail. This should be configurable. This should allow for guessing among several (explicitly listed) valid possibilities. To: indicates the recipient of the response. Date: indicate the date and time at which the response was composed. Subject: Auto-Re: (original subject) In-Reply-To: included if there was a message-id of the message. References: included as well. Context should be text/plain only. SMTP MAIL FROM: <> Auto-Submitted: auto-replied (reason) - When responses are sent Not when there is an Auto-Submitted: header with value of auto-replied or auto-generated. Not when there is a Precidence: bulk or Precidence: list header. Not to the same sender within a period of days (7 default). When a valid address for the recipient is in the To: Cc: or Bcc: headers. When the recipient is owner-*, *-request, "MAILER-DAEMON", etc (see procmail's FROM_DAEMON regexp). - Where responses are sent To the Return-Path: header only or _maybe_ the From_ field if enabled. = Minor Features - ListDetector -- like Perl's Mail::ListDetector to detect if a message is sent to a mailing list and if so what list. rubyfilter-0.12.orig/test/0000755001013300101330000000000007773224251017146 5ustar yaegashiyaegashi00000000000000rubyfilter-0.12.orig/test/rubyfilter/0000755001013300101330000000000007773224251021335 5ustar yaegashiyaegashi00000000000000rubyfilter-0.12.orig/test/rubyfilter/test-keyed_mailbox.rb0000444001013300101330000001230707773224251025454 0ustar yaegashiyaegashi00000000000000#-- # Copyright (C) 2002, 2003 Matt Armstrong. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # 3. The name of the author may not be used to endorse or promote # products derived from this software without specific prior # written permission. # # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE # GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER # IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR # OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN # IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # require 'test/rubyfilter/testbase' require 'rfilter/keyed_mailbox' require 'rmail/message' require 'ftools' module RFilter module Test class TestRFilter_KeyedMailbox < TestBase def stock_message message = RMail::Message.new message.header['to'] = 'bob@example.net' message.header['from'] = 'sally@example.net' message.header['subject'] = 'rutabaga' message.body = 'message body' return message end def test_s_new assert_exception(ArgumentError) { RFilter::KeyedMailbox.new } obj = RFilter::KeyedMailbox.new(scratch_filename('test')) assert_instance_of(RFilter::KeyedMailbox, obj) assert_respond_to(:save, obj) assert_respond_to(:delete, obj) assert_respond_to(:expire, obj) end def test_path dir = scratch_filename('test_path_dir') box = RFilter::KeyedMailbox.new(dir) assert_equal(dir, box.path) assert(box.path.frozen?) end def test_save dir = scratch_filename('queue') message = stock_message queue = RFilter::KeyedMailbox.new(dir) key = queue.save(message) assert_match(/^[A-F\d][A-F\d-]+[A-F\d]$/, key) end def test_retrieve dir = scratch_filename('queue') message = stock_message queue = RFilter::KeyedMailbox.new(dir) key = queue.save(message) filename = queue.retrieve(key) assert_equal(File.join(dir, 'new'), File.dirname(filename)) assert(File::exists?(filename)) filename2 = File.join(dir, 'cur', File::basename(filename) + ':2') File::move(filename, filename2) assert_equal(filename2, queue.retrieve(key)) File::delete(filename2) assert_equal(nil, queue.retrieve(key)) File::delete(File.join(dir, '.index', key)) assert_equal(nil, queue.retrieve(key)) end def test_delete dir = scratch_filename('queue') message = stock_message queue = RFilter::KeyedMailbox.new(dir) key = queue.save(message) index = File.join(dir, '.index', key) file = queue.retrieve(key) assert(File::exists?(index)) assert(File::exists?(file)) queue.delete(key) assert_equal(false, File::exists?(index)) assert_equal(false, File::exists?(file)) assert_no_exception { queue.delete('ABDFAABBDF') } end def test_expire dir = scratch_filename('queue') message = stock_message queue = RFilter::KeyedMailbox.new(dir) key = queue.save(message) assert_not_nil(queue.retrieve(key)) queue.expire(1) assert_not_nil(queue.retrieve(key)) queue.expire(0) assert_equal(nil, queue.retrieve(key)) end def test_each_key dir = scratch_filename('queue') message = stock_message # Create a new queue with 10 separate messages queue = RFilter::KeyedMailbox.new(dir) keys = [0..10].collect { queue.save(message) } # Ensure unique keys are returned assert_equal(keys.length, keys.uniq.length, "duplicate key found") # Ensure each and every key is enumerated exactly once keys_copy = keys.dup queue.each_key { |key| assert(keys_copy.include?(key), "each_key returned a duplicate or random key") keys_copy.delete(key) } assert(keys_copy.empty?, "not all keys were enumerated") # Delete all the keys and make sure none are enumerated keys.each { |key| queue.delete(key) } queue.each_key { |key| assert(false, "enumerated a key after they were all deleted") } end end end end rubyfilter-0.12.orig/test/rubyfilter/testtagged.rb0000444001013300101330000001003307773224251024010 0ustar yaegashiyaegashi00000000000000# # Copyright (C) 2002, 2003 Matt Armstrong. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # 3. The name of the author may not be used to endorse or promote # products derived from this software without specific prior # written permission. # # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE # GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER # IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR # OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN # IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # require 'test/rubyfilter/testbase' begin require 'mail/tagged' class TestRFilter__AddressTagger < TestBase def address(s) RFilter::Address.parse(s)[0] end def test_tag_dated a1 = address("Bob Smith ") t = RFilter::AddressTagger.new("the key", "+", 10) a2 = t.dated(a1, Time.now + 60) assert_equal("bob", a1.local) assert_match(/^bob\+\d{12}\.d\.[\da-h]{20}$/, a2.local) # Test varying the strength a1 = address("Bob Smith ") t = RFilter::AddressTagger.new("the key", "+", 3) a2 = t.dated(a1, Time.now + 60) assert_equal("bob", a1.local) assert_match(/^bob\+\d{12}\.d\.[\da-h]{6}$/, a2.local) end def test_tag_keyword a1 = address("Bob Smith ") t = RFilter::AddressTagger.new("the key", "+", 10) a2 = t.keyword(a1, "the keyword") assert_equal("bob", a1.local) assert_match(/^bob\+the_keyword\.k\.[\da-h]{20}$/, a2.local) # Test varying the strength a1 = address("Bob Smith ") t = RFilter::AddressTagger.new("the key", "+", 3) a2 = t.keyword(a1, "the keyword") assert_equal("bob", a1.local) assert_match(/^bob\+the_keyword\.k\.[\da-h]{6}$/, a2.local) end def test_verify t = RFilter::AddressTagger.new("the key", "+", 7) assert(t.verify(address("bob+the_keyword.k.9ac20a3ca7a6fb@example.net"))) assert(t.verify(address("bob+the_keyword.typenomatter.9ac20a3ca7a6fb@example.net"))) assert(!t.verify(address("bob+the_keyword.k.9ac20a3ca7a6fe@example.net"))) assert(!t.verify(address("bob+the_keyword.k.8ac20a3ca7a6fb@example.net"))) assert(!t.verify(address("bob+a_keyword.k.9ac20a3ca7a6fb@example.net"))) e = assert_exception(ArgumentError) { t.verify(address("bob@example.net")) } assert_equal("address not tagged", e.message) e = assert_exception(ArgumentError) { t.verify(address("bob+the_keyword.+.9ac20a3ca7a6fb@example.net")) } assert_equal("address not tagged", e.message) e = assert_exception(ArgumentError) { t.verify(address("bob+.k.9ac20a3ca7a6fb@example.net")) } assert_equal("address not tagged", e.message) e = assert_exception(ArgumentError) { t.verify(address('"bob+the_keyword..9ac20a3ca7a6fb"@example.net')) } assert_equal("address not tagged", e.message) e = assert_exception(ArgumentError) { t.verify(address("bob+the_keyword.k.+@example.net")) } assert_equal("address not tagged", e.message) end end rescue LoadError end rubyfilter-0.12.orig/test/rubyfilter/runtests.rb0000444001013300101330000000305307773224251023550 0ustar yaegashiyaegashi00000000000000#!/usr/bin/env ruby # # Copyright (C) 2001, 2002, 2003 Matt Armstrong. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # 3. The name of the author may not be used to endorse or promote # products derived from this software without specific prior # written permission. # # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE # GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER # IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR # OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN # IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # $VERBOSE = true Dir[File.join(File.dirname(__FILE__), 'test*.rb')].each {|f| require f } rubyfilter-0.12.orig/test/rubyfilter/testdelivery_agent.rb0000444001013300101330000004306407773224251025570 0ustar yaegashiyaegashi00000000000000# # Copyright (C) 2001, 2002, 2003 Matt Armstrong. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # 3. The name of the author may not be used to endorse or promote # products derived from this software without specific prior # written permission. # # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE # GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER # IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR # OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN # IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # require 'test/rubyfilter/testbase' require 'rfilter/delivery_agent' module RFilter module Test class TestRFilterDeliveryAgent < TestBase @@count = 0 FROM_RE = /^From .*?@.*? (Mon|Tue|Wed|Thu|Fri|Sat|Sun) (Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) [ \d]\d \d{2}:\d{2}:\d{2} \d{4}/ def setup super @message_string = <<-EOF From: test-from@example.com To: test-to@example.com Subject: this is a test message From test EOF @message_string.freeze end # Make sure the maildir file holds the same message as # @message_string def validate_maildir_message(delivered_to) lines = IO.readlines(delivered_to) assert_equal("From: test-from@example.com\n", lines[0]) assert_equal("To: test-to@example.com\n", lines[1]) assert_equal("Subject: this is a test message\n", lines[2]) assert_equal("\n", lines[3]) assert_equal("From test\n", lines[4]) assert_equal(nil, lines[5]) end # Make sure the unix mbox file holds the same message as # @message_string def validate_mbox(mailbox) lines = IO.readlines(mailbox) assert_match(FROM_RE, lines[0]) lines.shift assert_equal("From: test-from@example.com\n", lines[0]) assert_equal("To: test-to@example.com\n", lines[1]) assert_equal("Subject: this is a test message\n", lines[2]) assert_equal("\n", lines[3]) assert_equal(">From test\n", lines[4]) assert_equal("\n", lines[5]) assert_equal(nil, lines[6]) end def new_lda() log = scratch_filename("lda-log") lda = string_as_file(@message_string) { |file| RFilter::DeliveryAgent.new(file, log) } assert_not_nil(lda) assert(test(?e, log)) [lda, log] end def errors_in_logfile(logfile) file_contains(logfile, /exception|backtrace|error/i) end def test_defer() begin defer_reason = "I might not like you any more" lda, log = new_lda e = assert_exception(RFilter::DeliveryAgent::DeliveryDefer) { lda.defer(defer_reason) } assert_equal(e.message, defer_reason) assert(file_contains(log, Regexp.new(Regexp.escape(defer_reason)))) assert(!errors_in_logfile(log)) end begin lda, log = new_lda e = assert_exception(RFilter::DeliveryAgent::DeliveryDefer) { lda.defer } assert_equal(e.message, "") assert(file_contains(log, "Action: defer:")) assert(!errors_in_logfile(log)) end end def test_reject() begin reject_reason = "I don't like you any more" lda, log = new_lda e = assert_exception(RFilter::DeliveryAgent::DeliveryReject) { lda.reject(reject_reason) } assert_equal(e.message, reject_reason) assert(file_contains(log, Regexp.new(Regexp.escape(reject_reason)))) assert(!errors_in_logfile(log)) end begin lda, log = new_lda e = assert_exception(RFilter::DeliveryAgent::DeliveryReject) { lda.reject } assert_equal(e.message, "") assert(file_contains(log, Regexp.new(Regexp.escape("Action: reject:")))) assert(!errors_in_logfile(log)) end end def test_message=() m = RMail::Message.new lda, log = new_lda assert_same(m, lda.message = m) assert_same(m, lda.message) end def process_boilerplate(nolog = false) log = scratch_filename("process-log") unless nolog log ||= nil string_as_file(@message_string) {|file| RFilter::DeliveryAgent.process(file, log) { |lda| yield(lda, log) } } end def test_process() log = nil # Test what happens when the lda script does nothing e = assert_exception(RFilter::DeliveryAgent::DeliveryDefer) { process_boilerplate { |lda, log| assert_not_nil(lda) assert_not_nil(log) # do nothing } } assert_equal("finished without a final delivery", e.message) assert(file_contains(log, /Action: defer: finished without a final delivery/)) # Test what happens when the lda script raises a random exception e = assert_exception(RFilter::DeliveryAgent::DeliveryDefer) { process_boilerplate { |lda, log| assert_not_nil(lda) assert_not_nil(log) lda.log(1, "about to raise an exception") raise "URP!" } } assert_equal("uncaught exception", e.message) assert(file_contains(log, /uncaught exception: .*RuntimeError.* URP!/)) assert(file_contains(log, /uncaught exception backtrace:/)) # Test what happens when the lda script raises a random exception # when there is no log e = assert_exception(RuntimeError) { process_boilerplate(true) { |lda, log| assert_not_nil(lda) assert_nil(log) lda.log(1, "about to raise an exception") raise "raised within the lda.process block" } } assert_equal("raised within the lda.process block", e.message) # Test what happens when the lda script actually delivers mailbox = scratch_filename("mailbox") e = assert_exception(RFilter::DeliveryAgent::DeliverySuccess) { process_boilerplate { |lda, log| assert_not_nil(lda) assert_not_nil(log) lda.save(mailbox) } } mailbox_re = Regexp::escape(mailbox.inspect) assert_match(/^saved to mbox #{mailbox_re}$/, e.message) assert(file_contains(log, /\bAction: save to #{mailbox_re}$/)) validate_mbox(mailbox) end def test_exitcode() e = assert_exception(ArgumentError) { RFilter::DeliveryAgent.exitcode(5) } assert_equal("argument is not a DeliveryComplete exception: 5 (Fixnum)", e.message) e = assert_exception(ArgumentError) { RFilter::DeliveryAgent.exitcode(RuntimeError.new("hi mom!")) } assert_match(/argument is not a DeliveryComplete exception: .*RuntimeError/, e.message) assert_equal(RFilter::MTA::EXITCODE_DELIVERED, RFilter::DeliveryAgent.exitcode(RFilter::DeliveryAgent::DeliverySuccess.new(""))) assert_equal(RFilter::MTA::EXITCODE_REJECT, RFilter::DeliveryAgent.exitcode(RFilter::DeliveryAgent::DeliveryReject.new(""))) assert_equal(RFilter::MTA::EXITCODE_DEFER, RFilter::DeliveryAgent.exitcode(RFilter::DeliveryAgent::DeliveryDefer.new(""))) assert_exception(ArgumentError) { RFilter::DeliveryAgent.exitcode(RFilter::DeliveryAgent::LoggingError.new("")) } assert_equal(RFilter::MTA::EXITCODE_DEFER, RFilter::DeliveryAgent.exitcode(RFilter::DeliveryAgent::DeliveryCommandFailure.new("", 1))) assert_equal(RFilter::MTA::EXITCODE_DEFER, RFilter::DeliveryAgent.exitcode(RFilter::DeliveryAgent::DeliveryComplete.new(""))) end def test_save() # Test successful delivery with a real message with no mbox_from mailbox = scratch_filename("mailbox") lda, log = new_lda lda.header.mbox_from = nil lda.save(mailbox, true) assert_equal(true, test(?e, mailbox)) assert_equal(true, test(?e, log)) assert(!errors_in_logfile(log)) validate_mbox(mailbox) # Test successful delivery with a real message that has a # mbox_from mailbox = scratch_filename("mailbox") lda, log = new_lda lda.header.mbox_from = "From matt@example.com Mon Dec 24 00:00:06 2001" lda.save(mailbox, true) assert_equal(true, test(?e, mailbox)) assert_equal(true, test(?e, log)) assert(!errors_in_logfile(log)) validate_mbox(mailbox) # Test successful delivery to a Maildir with a real message that # has a mbox_from mailbox = scratch_filename("maildir") + '/' lda, log = new_lda lda.header.mbox_from = "From matt@example.com Mon Dec 24 00:00:06 2001" delivered_to = lda.save(mailbox, true) assert(!errors_in_logfile(log)) validate_maildir_message(delivered_to) # Test successful delivery with a real message to an mbox, # continue = false mailbox = scratch_filename("mailbox") assert_equal(false, test(?e, mailbox)) lda, log = new_lda e = assert_exception(RFilter::DeliveryAgent::DeliverySuccess) { lda.save(mailbox, false) } assert_equal("saved to mbox #{mailbox.inspect}", e.message) assert_equal(true, test(?e, mailbox)) assert_equal(true, test(?e, log)) assert(!errors_in_logfile(log)) validate_mbox(mailbox) # Test successful delivery with a real message to a maildir, # continue = false mailbox = scratch_filename("maildir") + '/' lda, log = new_lda e = assert_exception(RFilter::DeliveryAgent::DeliverySuccess) { lda.save(mailbox, false) } assert_equal("saved to mbox #{mailbox.inspect}", e.message) assert(!errors_in_logfile(log)) end def test_deliver_pipe catfile = scratch_filename("catfile.pipe") logfile = scratch_filename("logfile.pipe") command = "/bin/cat > #{catfile}" lda, log = new_lda assert_equal(false, test(?e, catfile)) assert_equal(true, test(?e, log)) lda.pipe(command, true) assert(test(?e, catfile)) assert_equal(0, $?, "exit value not propagated") command_re = Regexp::escape(command.inspect) assert(file_contains(log, /\bAction: pipe to #{command_re}/)) assert(!errors_in_logfile(log)) # test that a successful pipe delivery will try to exit command = "/bin/cat >> #{catfile}" e = assert_exception(RFilter::DeliveryAgent::DeliverySuccess) { lda.pipe(command) } assert_equal("pipe to #{command.inspect}", e.message) assert(file_contains(log, /\bAction: pipe to #{Regexp::escape(command.inspect)}/)) assert(!errors_in_logfile(log)) end def test_deliver_pipe_error # test with continue = true lda, log = new_lda command = "/bin/sh -c \"exit 32\"" e = assert_exception(RFilter::DeliveryAgent::DeliveryCommandFailure) { lda.pipe(command, true) } assert_equal(32 << 8, e.status) assert_equal("pipe failed for command #{command.inspect}", e.message) command_re = Regexp::escape(command.inspect) assert(file_contains(log, /\bAction: pipe to #{command_re}/)) assert(errors_in_logfile(log)) # test with continue = false lda, log = new_lda command = "/bin/sh -c \"exit 1\"" e = assert_exception(RFilter::DeliveryAgent::DeliveryCommandFailure) { lda.pipe(command, false) } assert_equal("pipe failed for command #{command.inspect}", e.message) assert_equal(1 << 8, e.status) command_re = Regexp::escape(command.inspect) assert(file_contains(log, /\bAction: pipe to #{command_re}/)) end def test_deliver_filter_simple lda, log = new_lda command = [ ruby_program, '-pe', ';' ] old_message = lda.message lda.filter(*command) assert(old_message.__id__ != lda.message.__id__) assert_equal(old_message, lda.message) assert(!errors_in_logfile(log)) end def test_deliver_filter_epipe() lda, log = new_lda script = <<-EOF while line = gets # exit early without reading all of the contents exit(0) if line =~ /^Subject:/i puts line end EOF command = [ ruby_program, '-pe', script ] old_message = lda.message lda.filter(*command) assert(old_message.__id__ != lda.message.__id__) assert(old_message != lda.message) assert(old_message.header.field?('subject')) assert(!lda.message.header.field?('subject')) assert(!errors_in_logfile(log)) end def test_deliver_filter_failed() lda, log = new_lda script = <<-EOF while line = gets # exit early without reading all of the contents exit(60) if line =~ /^Subject:/i puts line end EOF command = [ ruby_program, '-pe', script ] old_message = lda.message e = assert_exception(RFilter::DeliveryAgent::DeliveryCommandFailure) { lda.filter(*command) } assert_match(/failed.*exit\(60\)/, e.message) assert_same(old_message, lda.message) assert(errors_in_logfile(log)) end def test_deliver_filter_failed2() lda, log = new_lda command = [ '/this/command/does/not/exist' ] old_message = lda.message e = assert_exception(RFilter::DeliveryAgent::DeliveryCommandFailure) { lda.filter(*command) } assert_match(/failed for command.*does\/not\/exist/, e.message) assert_same(old_message, lda.message) assert(errors_in_logfile(log)) end def test_nil_log lda = nil string_as_file(@message_string) { |f| lda = RFilter::DeliveryAgent.new(f, nil) } lda.logging_level = 10 lda.log(1, "this is ignored") e = assert_exception(RFilter::DeliveryAgent::LoggingError) { lda.log(0, "this should raise an exception") } assert_match(/failed to log high priority message: this should raise an exception/, e.message) assert_equal(nil, e.original_exception) end def test_log lda, log = new_lda # Default logging level is 2 assert_equal(2, lda.logging_level) # First make sure we get errors when we set log level to # something bogus. assert_exception(ArgumentError) { lda.logging_level = "foo" } assert_exception(TypeError) { lda.logging_level = Object.new } e = assert_exception(ArgumentError) { lda.logging_level = -1 } assert_match(/invalid logging level value -1/, e.message) e = assert_exception(ArgumentError) { lda.logging_level = 0 } assert_match(/invalid logging level value 0/, e.message) 1.upto(5) { |logset| lda.logging_level = logset 0.upto(5) { |logat| lda.log(logat, "set#{logset} at#{logat}") } } 1.upto(5) { |log_level| 0.upto(5) { |logat| re = /: set#{log_level} at#{logat}/ contains = file_contains(log, re) if logat <= log_level assert(contains) else assert(!contains) end } } end # Test message access functions of RFilter::DeliveryAgent def test_message_access lda, log = new_lda # Test the message method, to get at the message itself assert_respond_to(:message, lda) assert_not_nil(lda.message) assert_respond_to(:body, lda.message) assert_respond_to(:header, lda.message) assert_kind_of(RMail::Message, lda.message) # Test the header method, to get at the message header itself assert_respond_to(:header, lda) assert_not_nil(lda.header) assert_respond_to(:add, lda.header) assert_kind_of(RMail::Header, lda.header) assert_same(lda.message.header, lda.header) # Test the body method, to get at the message body itself assert_respond_to(:body, lda) assert_not_nil(lda.body) assert_respond_to(:each, lda.body) assert_respond_to(:grep, lda.body) assert_kind_of(String, lda.body) assert_same(lda.message.body, lda.body) end end end end rubyfilter-0.12.orig/test/rubyfilter/testdeliver.rb0000444001013300101330000005176307773224251024226 0ustar yaegashiyaegashi00000000000000# # Copyright (C) 2001, 2002, 2003 Matt Armstrong. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # 3. The name of the author may not be used to endorse or promote # products derived from this software without specific prior # written permission. # # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE # GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER # IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR # OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN # IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # require 'test/rubyfilter/testbase' require 'rfilter/deliver' require 'tempfile' module RFilter module Test class TestRFilterDeliver < TestBase FROM_RE = /^From \S+@\S+ (Mon|Tue|Wed|Thu|Fri|Sat|Sun) (Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) [ \d]\d \d{2}:\d{2}:\d{2} \d{4}$/ # Validates an mbox style mailbox and returns the number of messages # it contains. def validate_mbox(filename, sentinel) mailcount = 0 line_count = 1 message_line_count = 0 body_line_count = -1 prevline = nil sentinel_match = true IO.foreach(filename) { |line| assert(line[-1] == ?\n, "Line #{line_count} #{line.inspect} does not end in a newline") if (line_count == 1) assert_match(line, FROM_RE, 'invalid From_') end if ((line_count == 1 || prevline == "\n") && line =~ /^From /) mailcount += 1 message_line_count = 0 assert(prevline.nil? || body_line_count >= 0, "No body found") body_line_count = -1 unless sentinel.nil? assert(sentinel_match, "RFilter did not contain sentinel " + sentinel.inspect) sentinel_match = false end end if body_line_count < 0 && line =~ /^$/ body_line_count = 0 end unless sentinel.nil? if line =~ sentinel sentinel_match = true end end if (message_line_count == 1) assert_match(line, /^\S+:/, "Message at line #{line_count} does not begin " + "with headers") end prevline = line line_count += 1 message_line_count += 1 body_line_count += 1 if body_line_count >= 0 } assert(sentinel_match, "RFilter did not contain sentinel " + sentinel.inspect) unless sentinel.nil? return mailcount end def test_deliver_mbox_atime_preserve() mailbox = File.join(scratch_dir, "mbox.atime_preserve") File.open(mailbox, 'w') {} old_time = Time.at(0) File.utime(old_time, File.stat(mailbox).mtime, mailbox) assert_equal(old_time, File.stat(mailbox).atime) RFilter::Deliver.deliver_mbox(mailbox, "message") assert_equal(old_time, File.stat(mailbox).atime, "failed to preserve access time on mailbox") end def test_deliver_mbox_dev_null() assert_equal('/dev/null', RFilter::Deliver.deliver_mbox('/dev/null', "message")) end def test_deliver_mbox_no_each_method() mailbox = File.join(scratch_dir, "mbox.no_each_method") assert(!test(?e, mailbox)) e = assert_exception(NO_METHOD_ERROR) { RFilter::Deliver.deliver_mbox(mailbox, nil) } assert_not_nil(e) assert_match(/undefined method `each'/, e.message) assert(test(?f, mailbox)) assert(test(?z, mailbox)) assert_equal(0, validate_mbox(mailbox, nil)) e = assert_exception(NO_METHOD_ERROR) { RFilter::Deliver.deliver_mbox(mailbox, Object.new) } assert_not_nil(e) assert(test(?f, mailbox)) assert(test(?z, mailbox)) assert_equal(0, validate_mbox(mailbox, nil)) assert_match(/undefined method `each'/, e.message) end def test_deliver_mbox_string_with_from() mailbox = File.join(scratch_dir, "mbox.string_with_from") assert(!test(?e, mailbox)) string_message = "From baz@bango Fri Nov 9 23:00:43 2001\nX-foo: foo\n\nfoo" RFilter::Deliver.deliver_mbox(mailbox, string_message) assert(test(?f, mailbox)) assert_equal(string_message.length + 2, test(?s, mailbox)) assert_equal(1, validate_mbox(mailbox, /^From baz@bango/)) end def test_deliver_mbox_retval() mailbox = scratch_filename('mbox.retval-test') assert_equal(mailbox, ::RFilter::Deliver.deliver_mbox(mailbox, "")) end def test_deliver_mbox_string_without_from() mailbox = File.join(scratch_dir, "mbox.string_without_from") assert(!test(?e, mailbox)) string_message = "X-foo: foo\n\nfoo" RFilter::Deliver.deliver_mbox(mailbox, string_message) assert(test(?f, mailbox)) assert_equal("From foo@bar Fri Nov 9 23:00:43 2001\n".length + string_message.length + 2, test(?s, mailbox)) assert_equal(1, validate_mbox(mailbox, /^foo$/)) end def test_deliver_mbox_array_with_from() mailbox = File.join(scratch_dir, "mbox.array_with_from") assert(!test(?e, mailbox)) array_message = [ 'From baz@bango Fri Nov 9 23:00:43 2001', 'X-foo: foo', '', 'foo' ] RFilter::Deliver.deliver_mbox(mailbox, array_message) assert(test(?f, mailbox)) assert_equal(array_message.join("\n").length + 2, test(?s, mailbox)) assert_equal(1, validate_mbox(mailbox, /^foo$/)) end def test_deliver_mbox_array_without_from() mailbox = File.join(scratch_dir, "mbox.array_without_from") assert(!test(?e, mailbox)) array_message = [ 'X-foo: foo', '', 'foo' ] RFilter::Deliver.deliver_mbox(mailbox, array_message) assert(test(?f, mailbox)) assert_equal("From baz@bar Fri Nov 9 23:00:43 2001\n".length + array_message.join("\n").length + 2, test(?s, mailbox)) assert_equal(1, validate_mbox(mailbox, /^foo$/)) end def test_deliver_mbox_complex() mailbox = scratch_filename("mbox.complex") obj = Object.new def obj.each yield "x-header: header value" yield "" yield "complex body text" yield "complex body text again" yield "From is escaped" yield "From. not escaped" end def obj.mbox_from "From complex@object Fri Nov 9 23:00:43 2001" end assert(!test(?e, mailbox)) RFilter::Deliver.deliver_mbox(mailbox, obj) RFilter::Deliver.deliver_mbox(mailbox, obj) assert(test(?f, mailbox)) assert_equal(282, test(?s, mailbox)) assert_equal(2, validate_mbox(mailbox, /^complex body text again$/)) File.open(mailbox) {|f| # make sure leading body "From " lines are escaped f.grep(/is escaped/).each {|escaped| assert_match(/^>From /, escaped) } # but not all "From" lines are escaped f.grep(/.?From[^ ]/).each {|escaped| assert_not_match(/^>From /, escaped) } # make sure the From_ headers are what obj.get_mbox_from returns f.grep(/From /).each {|from| assert_equal(obj.mbox_from + "\n", from) } } end def test_deliver_mbox_flock_timeout() mailbox = scratch_filename("mbox") File.open(mailbox, 'w') { |file| file.flock(File::LOCK_EX) e = assert_exception(RFilter::Deliver::LockingError) { RFilter::Deliver.deliver_mbox(mailbox, "message") } assert_equal("Timeout locking mailbox.", e.message) } end def test_deliver_mbox_random_exception() mailbox = scratch_filename("mbox") obj = Object.new def obj.each 10.times { |i| yield "Header#{i}: foo bar baz" } raise "No more already!" end e = assert_exception(RuntimeError) { RFilter::Deliver.deliver_mbox(mailbox, obj) } assert_equal("No more already!", e.message) assert_equal(0, File::stat(mailbox).size) RFilter::Deliver.deliver_mbox(mailbox, "this\nis\na\nmessage!") size = File::stat(mailbox).size e = assert_exception(RuntimeError) { RFilter::Deliver.deliver_mbox(mailbox, obj) } assert_equal("No more already!", e.message) assert_equal(size, File::stat(mailbox).size) end def test_write_to_mbox mailbox = scratch_filename('write_to_mbox') assert(!test(?e, mailbox)) string_message = "X-foo: foo\n\nfoo" File.open(mailbox, 'w') { |f| RFilter::Deliver.write_to_mbox(f, string_message) } assert(test(?f, mailbox)) assert_equal(1, validate_mbox(mailbox, /^foo$/)) end def test_deliver_filter_simple() script = <<-EOF while line = gets puts "child got " + line end EOF status = RFilter::Deliver.deliver_filter("foo\nbar\nbaz", ruby_program, '-e', script) { |io| assert_equal([ "child got foo\n", "child got bar\n", "child got baz\n" ], io.readlines) } assert_equal(0, status) end def deliver_filter_epipe_help(input) status = RFilter::Deliver.deliver_filter(input, ruby_program, '-e', "exit 0") { |io| assert_equal([], io.readlines) } assert_equal(0, status) end def test_deliver_filter_epipe() deliver_filter_epipe_help("") deliver_filter_epipe_help(".") deliver_filter_epipe_help("foo bar baz\n" * 1024 * 500) end def test_deliver_filter_noexists() status = RFilter::Deliver.deliver_filter("input", "/does/not/exist") { |io| assert_equal([], io.readlines) } if status.respond_to?(:exited?) # ruby 1.8 assert(status.exited?) assert(1 == status.exitstatus || 255 == status.exitstatus) else # before ruby 1.8 assert_equal((255 << 8), status) end end def test_deliver_filter_badexit() status = RFilter::Deliver.deliver_filter("input", ruby_program, '-e', "exit 5") { |io| assert_equal([], io.readlines) } assert_equal(5 << 8, status) end def test_deliver_pipe_no_each_method() output = File.join(scratch_dir, "pipe.no_each_method") command = "/bin/cat > #{output}" assert_equal(false, test(?e, output)) e = assert_exception(NO_METHOD_ERROR) { RFilter::Deliver.deliver_pipe(command, nil) } assert_not_nil(e) assert_match(/undefined method `each'/, e.message) assert_equal(true, test(?e, output)) assert_equal(true, test(?z, output)) e = assert_exception(NO_METHOD_ERROR) { RFilter::Deliver.deliver_pipe(command, Object.new) } assert_not_nil(e) assert_match(/undefined method `each'/, e.message) assert_equal(true, test(?e, output)) assert_equal(true, test(?z, output)) end def test_deliver_pipe_simple() output = File.join(scratch_dir, "pipe.simple") command = "/bin/cat > #{output}" message1 = "This is message one." assert_equal(false, test(?e, output)) RFilter::Deliver.deliver_pipe(command, message1) got = File.open(output).readlines().join('') assert_equal(message1 + "\n", got) message2 = %q{This is message two. It has some newlines in it. And it even ends with one. } RFilter::Deliver.deliver_pipe(command, message2) got = File.open(output).readlines().join('') assert_equal(message2, got) message3 = [ "Line 1", "Line 2", "", "Line 3" ] RFilter::Deliver.deliver_pipe(command, message3) got = File.open(output).readlines().join('') assert_equal(message3.join("\n") + "\n", got) end def test_deliver_pipe_failed() command = "/bin/sh -c 'exit 7'" RFilter::Deliver.deliver_pipe(command, "irrelevant message") assert_equal(7 << 8, $?) # now attempt to generate an EPIPE pipe error long_message = [] 0.upto(5000) { long_message.push("This is a line of text") } RFilter::Deliver.deliver_pipe(command, long_message) assert_equal(7 << 8, $?) end def test_deliver_maildir require 'socket' dir = scratch_filename('Maildir') delivered_to = RFilter::Deliver.deliver_maildir(dir, 'message') assert(FileTest::directory?(dir)) assert(FileTest::directory?(File.join(dir, 'tmp'))) assert(FileTest::directory?(File.join(dir, 'new'))) assert(FileTest::directory?(File.join(dir, 'cur'))) # # Make sure the file is delivered to the right filename # assert_kind_of(String, delivered_to) newdir_re = Regexp.escape(File.join(dir, 'new')) hostname_re = Regexp.escape(Socket::gethostname) pid_re = Regexp.escape(Process::pid.to_s) assert_match(/#{newdir_re}\/\d+\.M[A-H0-9]+P#{pid_re}Q\d+\.#{hostname_re}$/, delivered_to) /#{newdir_re}\/(\d+)/ =~ delivered_to assert_operator(10, '>', Time.now.to_i - Integer($1).to_i) assert_operator(0, '<=', Time.now.to_i - Integer($1).to_i) # # Make sure that file contains the message # assert(FileTest::file?(delivered_to)) lines = IO::readlines(delivered_to) assert_equal("message\n", lines[0]) assert_nil(lines[1]) # # Make sure the tmp name is gone # assert(!FileTest::file?(File.join(dir, 'tmp', File::basename(delivered_to)))) end def test_deliver_maildir_twice dir = scratch_filename('Maildir') first = RFilter::Deliver.deliver_maildir(dir, 'message_first') second = RFilter::Deliver.deliver_maildir(dir, 'message_second') # # Validate that the filenames look sane # assert(!(first == second)) assert_kind_of(String, first) assert_kind_of(String, second) File.basename(first) =~ /Q(\d+)/ first_seq = $1 File.basename(second) =~ /Q(\d+)/ second_seq = $1 assert(Integer(first_seq) + 1 == Integer(second_seq)) # # Validate that the messages look sane # assert_equal("message_first\n", IO::readlines(first)[0]) assert_nil(IO::readlines(first)[1]) assert_equal("message_second\n", IO::readlines(second)[0]) assert_nil(IO::readlines(second)[1]) end def test_deliver_maildir_with_mbox_from message = <<-EOF From bob@example content EOF delivered_to = RFilter::Deliver.deliver_maildir(scratch_filename('Maildir'), message) assert_equal("content\n", IO::readlines(delivered_to)[0]) assert_nil(IO::readlines(delivered_to)[1]) end # Since we now include microseconds in the temp file name, this # test is much harder to do. Disabled. =begin def test_deliver_maildir_one_tmp_file_conflict dir = scratch_filename('Maildir') # First, figure out what sequence number we're at sequence = maildir_sequence_from_file(RFilter::Deliver.deliver_maildir(dir, 'foo')) sequence = sequence.succ # Next create the next possible tmp file conflicting = maildir_fill_tmp_files(dir, sequence, 1) # Then deliver start_time = Time.now delivered_to = RFilter::Deliver.deliver_maildir(dir, 'the message') end_time = Time.now # Make sure the conflicting temp files didn't get clobbered maildir_verify_conflicting_tmp_files(conflicting) # Make sure we didn't sleep too long assert_operator(1.5, '<', end_time - start_time, "Did not sleep long enough.") assert_operator(4.0, '>', end_time - start_time, "Slept too long.") end =end # Since we now include microseconds in the temp file name, this # test is much harder to do. Disabled. =begin def test_deliver_maildir_too_many_tmp_file_conflicts # Tests that if all possible tmp files are present, the function # throws an exception. dir = scratch_filename('Maildir') # First, figure out what sequence number we're at sequence = maildir_sequence_from_file(RFilter::Deliver.deliver_maildir(dir, 'foo')) sequence = sequence.succ # Next create the next possible tmp file. The delivery won't take # more than 10 seconds, so create 11 seconds worth of tmp files. conflicting = maildir_fill_tmp_files(dir, sequence, 11) # Then deliver start_time = Time.now assert_exception(RuntimeError) { RFilter::Deliver.deliver_maildir(dir, 'the message') } end_time = Time.now # Make sure the conflicting temp files didn't get clobbered maildir_verify_conflicting_tmp_files(conflicting) # Make sure we didn't sleep too long assert_operator(3.75, '<', end_time - start_time, "Did not sleep long enough.") assert_operator(4.5, '>', end_time - start_time, "Slept too long.") end =end def test_deliver_maildir_is_file dir = scratch_filename('Maildir') File.open(dir, "w") { |f| f.puts "this is a file, not a directory" } assert(FileTest::file?(dir)) e = assert_exception(Errno::EEXIST) { RFilter::Deliver.deliver_maildir(dir, 'message') } assert_match(/File exists.*#{Regexp::escape(dir)}/, e.message) end def maildir_fill_tmp_files(dir, sequence, seconds_count) now = Time::now.to_i conflicting = [] now.upto(now + seconds_count) { |time| name = sprintf("%d.%d_%d.%s", time, Process::pid, sequence, Socket::gethostname) tmp_name = File.join(dir, 'tmp', name) conflicting << tmp_name File.open(tmp_name, 'w') { |f| f.puts "conflicting temp file" } } conflicting end def maildir_verify_conflicting_tmp_files(conflicting) conflicting.each { |conflicting_tmp| assert(FileTest::file?(conflicting_tmp), "Conflicting temp file got deleted.") assert_equal("conflicting temp file\n", IO::readlines(conflicting_tmp)[0], "Conflicting temp file got modified.") assert_nil(IO::readlines(conflicting_tmp)[1], "Conflicting temp file was appended to.") } end def maildir_sequence_from_file(file) # First, figure out what sequence number we're at if File.basename(file) =~ /^\d+\..*Q(\d+)\./ $1 else raise "#{file} isn't valid" end end def helper_for_new_cur_tmp_is_file(subdir) dir = scratch_filename('Maildir') Dir.mkdir(dir) file = File.join(dir, subdir) File.open(file, 'w') { |f| f.puts "this is a file, not a directory" } assert(FileTest::file?(file)) e = assert_exception(Errno::EEXIST) { RFilter::Deliver.deliver_maildir(dir, 'message') } assert_match(/File exists.*#{Regexp::escape(file)}/, e.message) end def test_deliver_maildir_new_is_file helper_for_new_cur_tmp_is_file('new') end def test_deliver_maildir_cur_is_file helper_for_new_cur_tmp_is_file('cur') end def test_deliver_maildir_tmp_is_file helper_for_new_cur_tmp_is_file('tmp') end end end end rubyfilter-0.12.orig/test/rubyfilter/test-rdeliver.rb0000444001013300101330000002536307773224251024462 0ustar yaegashiyaegashi00000000000000#-- # Copyright (C) 2002, 2003 Matt Armstrong. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # 3. The name of the author may not be used to endorse or promote # products derived from this software without specific prior # written permission. # # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE # GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER # IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR # OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN # IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # require 'test/rubyfilter/testbase' require 'rfilter/mta' module RFilter module Test class TestRDeliver < TestBase attr_reader :home def command_line_load_path(path) format("-I '%s'", path) end def rubymail_load_path extra_load_paths.collect { |path| command_line_load_path(path) }.join(' ') end def setup super @old_home = ENV['HOME'] @home = File.join(@scratch_dir, 'home') Dir.mkdir(@home) ENV['HOME'] = @home end def teardown ENV['HOME'] = @old_home super end def script File.join(Dir.getwd, 'bin', 'rdeliver.rb') end def test_rdeliver_syntax assert_equal(true, system(ruby_program + ' -c ' + script + ' >/dev/null')) end def test_rdeliver_home_arg home2 = home + '2' Dir.mkdir(home2) assert_equal(false, system(ruby_program, script, '--home', home2, '--bogus')) assert_equal(75 << 8, $?) catastrophic = File.join(home2, 'CATASTROPHIC_DELIVERY_FAILURE') assert(test(?e, catastrophic)) assert(file_contains(catastrophic, /unrecognized option.*--bogus/)) end def test_rdeliver_load_path_arg Dir.mkdir(File.join(scratch_dir, 'rfilter')) File.open(File.join(scratch_dir, 'rfilter', 'delivery_agent.rb'), 'w') { |file| file.puts 'raise "test succeeded"' } assert_equal(false, system(ruby_program, script, '--load-path', scratch_dir)) assert_equal(75 << 8, $?) catastrophic = File.join(home, 'CATASTROPHIC_DELIVERY_FAILURE') assert(test(?e, catastrophic)) assert(file_contains(catastrophic, /test succeeded/)) end def test_rdeliver_bad_arg assert_equal(false, system(ruby_program, script, '--bogus')) assert_equal(75 << 8, $?) catastrophic = File.join(home, 'CATASTROPHIC_DELIVERY_FAILURE') assert(test(?e, catastrophic)) assert(file_contains(catastrophic, /unrecognized option.*--bogus/)) end def test_extra_arguments assert_equal(false, system(ruby_program, script, 'first', 'extra1', 'extra2')) assert_equal(75 << 8, $?) catastrophic = File.join(home, 'CATASTROPHIC_DELIVERY_FAILURE') assert(test(?e, catastrophic)) assert(file_contains(catastrophic, /RuntimeError/)) assert(file_contains(catastrophic, /extra arguments passed to.*\["extra1", "extra2"\]/)) end def test_homedir_chdir_failure Dir.rmdir(home) errors = scratch_filename('errors') assert_equal(false, system(format("'%s' '%s' --bogus > %s", ruby_program, script, errors))) assert_equal(75 << 8, $?) catastrophic = File.join(home, 'CATASTROPHIC_DELIVERY_FAILURE') assert_equal(false, test(?e, catastrophic)) assert(file_contains(errors, /unrecognized option.*--bogus/)) assert(file_contains(errors, /Failed writing CATASTROPHIC/)) assert(file_contains(errors, /Errno::ENOENT/)) assert(file_contains(errors, Regexp.new(Regexp.escape(home)))) end def do_test_no_config_file(config) cmd = format("'%s' '%s' -I '%s' %s %s < /dev/null", ruby_program, script, Dir.getwd, rubymail_load_path, config.nil? ? '' : config) assert_equal(false, system(cmd)) assert_equal(75 << 8, $?) catastrophic = File.join(home, 'CATASTROPHIC_DELIVERY_FAILURE') assert(test(?e, catastrophic)) assert(file_contains(catastrophic, /Errno::ENOENT/)) temp = config temp ||= '.rdeliver' assert(file_contains(catastrophic, Regexp.new(Regexp.escape(File.join(home, temp))))) end def test_no_dot_rdeliver assert_equal(false, system(format("'%s' '%s' -I '%s' %s < /dev/null", ruby_program, script, Dir.getwd, rubymail_load_path))) assert_equal(75 << 8, $?) catastrophic = File.join(home, 'CATASTROPHIC_DELIVERY_FAILURE') assert(test(?e, catastrophic)) assert(file_contains(catastrophic, /Errno::ENOENT/)) assert(file_contains(catastrophic, Regexp.new(Regexp.escape(File.join(home, '.rdeliver'))))) end def test_no_dot_rdeliver2 do_test_no_config_file(nil) end def test_no_config_file do_test_no_config_file("my-config") end def test_successful_deliver config_file = scratch_filename('config') File.open(config_file, 'w') { |f| f.puts <<-EOF def main agent.save('INBOX') end EOF } message = <<-EOF From: bob@example.net To: sally@example.net Subject: test message This is a test message EOF log = scratch_filename('log') command = format("'%s' '%s' -I '%s' %s -l '%s' '%s'", ruby_program, script, Dir.getwd, rubymail_load_path, log, config_file) IO.popen(command, 'w') { |io| message.each_line { |line| line = line.sub(/^\s+/, '') io.puts(line) } } assert_equal(0, $?) assert(test(?e, log)) inbox = File.join(home, 'INBOX') assert(test(?e, log)) assert(test(?e, inbox)) assert(file_contains(log, /action.*save to.*INBOX/i)) # FIXME: need a generic 'test a valid mbox' method assert(file_contains(inbox, /^Subject: test message$/)) assert(file_contains(inbox, /^From .*\d{4}$/)) assert(file_contains(inbox, /^This is a test message$/)) end def test_deferred_deliver_message config_file = scratch_filename('config') File.open(config_file, 'w') { |f| f.puts <<-EOF def main agent.defer("Please try again. Sorry!") end EOF } message = "" log = scratch_filename('log') stdout = scratch_filename('stdout') command = format("'%s' '%s' -I '%s' %s -l '%s' '%s' > '%s'", ruby_program, script, Dir.getwd, rubymail_load_path, log, config_file, stdout) IO.popen(command, 'w') { } assert_equal(RFilter::MTA::EXITCODE_DEFER << 8, $?) assert(test(?e, log)) assert(test(?e, stdout)) assert(file_contains(log, /action.*defer.*Please try again. Sorry!/i)) assert(file_contains(stdout, /^Please try again. Sorry!$/)) end def test_deferred_deliver_default config_file = scratch_filename('config') File.open(config_file, 'w') { |f| f.puts <<-EOF def main agent.defer end EOF } message = "" log = scratch_filename('log') stdout = scratch_filename('stdout') command = format("'%s' '%s' -I '%s' %s -l '%s' '%s' > '%s'", ruby_program, script, Dir.getwd, rubymail_load_path, log, config_file, stdout) IO.popen(command, 'w') { |io| } assert_equal(RFilter::MTA::EXITCODE_DEFER << 8, $?) assert(test(?e, stdout)) assert(test(?z, stdout)) assert(test(?e, log)) assert(file_contains(log, /action.*defer/i)) end def test_rejected_deliver_message config = <<-EOF def main agent.reject("We don't accept SPAM here.") end EOF string_as_file(config, true) { |config_file| message = "" log = scratch_filename('log') stdout = scratch_filename('stdout') command = format("'%s' '%s' -I '%s' %s -l '%s' '%s' > '%s'", ruby_program, script, Dir.getwd, rubymail_load_path, log, config_file.path, stdout) IO.popen(command, 'w') { |io| } assert_equal(RFilter::MTA::EXITCODE_REJECT << 8, $?) assert(test(?e, log)) assert(test(?e, stdout)) assert(file_contains(log, /action.*reject.*We don't accept SPAM here\./i)) assert(file_contains(stdout, /^We don't accept SPAM here\.$/)) } end def test_rejected_deliver_message2 config = <<-EOF def main agent.reject end EOF string_as_file(config, true) { |config_file| message = "" log = scratch_filename('log') stdout = scratch_filename('stdout') command = format("'%s' '%s' -I '%s' %s -l '%s' '%s' > '%s'", ruby_program, script, Dir.getwd, rubymail_load_path, log, config_file.path, stdout) IO.popen(command, 'w') { |io| } assert_equal(RFilter::MTA::EXITCODE_REJECT << 8, $?) assert(test(?e, stdout)) assert(test(?z, stdout)) assert(test(?e, log)) assert(file_contains(log, /action.*reject/i)) } end end end end rubyfilter-0.12.orig/test/rubyfilter/testtestbase.rb0000444001013300101330000001010407773224251024366 0ustar yaegashiyaegashi00000000000000# # Copyright (C) 2001, 2002, 2003 Matt Armstrong. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # 3. The name of the author may not be used to endorse or promote # products derived from this software without specific prior # written permission. # # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE # GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER # IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR # OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN # IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # # Test the TestBase class itself require 'test/rubyfilter/testbase.rb' module RFilter module Test class TestTestBase < TestBase def test_cleandir Dir.mkdir("_testdir_") Dir.mkdir("_testdir_/testsubdir") File.open("_testdir_/testfile", "w") { |file| file.puts "some data" } File.open("_testdir_/testsubdir/testfile", "w") { |file| file.puts "some data" } assert(test(?e, "_testdir_")) assert(test(?e, "_testdir_/testsubdir")) assert(test(?e, "_testdir_/testsubdir/testfile")) assert(test(?e, "_testdir_/testfile")) cleandir("_testdir_") assert(test(?e, "_testdir_")) assert(!test(?e, "_testdir_/testsubdir")) assert(!test(?e, "_testdir_/testsubdir/testfile")) assert(!test(?e, "_testdir_/testfile")) assert_equal(0, Dir.delete('_testdir_')) end def test_file_contains scratch = scratch_filename("file_contains") File.open(scratch, "w") { |f| f.puts "contains AAA" f.puts "contains BBB" } assert(file_contains(scratch, /BBB/)) assert(file_contains(scratch, "AAA")) assert(file_contains(scratch, /contains AAA/)) assert(file_contains(scratch, "contains BBB")) assert_equal(false, file_contains(scratch, /contains CCC/)) assert_equal(false, file_contains(scratch, "contains CCC")) end def test_ruby_program assert_not_nil(ruby_program) assert_kind_of(String, ruby_program) end def verify_scratch_dir_name(dir) assert_match(/scratch.*TestTestBase.*test/, dir) end def test_scratch_dir assert_not_nil(scratch_dir) assert_kind_of(String, scratch_dir) verify_scratch_dir_name(scratch_dir) end def test_scratch_filename name = scratch_filename("foobar") assert_kind_of(String, name) verify_scratch_dir_name(File.dirname(name)) assert_equal("foobar", File.basename(name)) name = scratch_filename("foobar") assert_kind_of(String, name) verify_scratch_dir_name(File.dirname(name)) assert_equal("foobar.1", File.basename(name)) end def test_string_as_file string = "yo\nman\n" string_as_file(string) { |f| assert_equal(string, f.readlines.join('')) } string2 = " yo\n man\n" string_as_file(string2) { |f| assert_equal("yo\nman\n", f.readlines.join('')) } string_as_file(string2, false) { |f| assert_equal(string2, f.readlines.join('')) } end end end end rubyfilter-0.12.orig/test/rubyfilter/testbase.rb0000444001013300101330000001464607773224251023505 0ustar yaegashiyaegashi00000000000000#-- # Copyright (C) 2001, 2002, 2003 Matt Armstrong. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # 3. The name of the author may not be used to endorse or promote # products derived from this software without specific prior # written permission. # # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE # GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER # IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR # OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN # IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # # Base for all the test cases, providing a default setup and teardown $VERBOSE = true require 'rubyunit' require 'rbconfig' require 'tempfile' require 'find' begin require 'pp' rescue LoadError end begin rubymail_lib = File.expand_path('../rubymail/lib') if FileTest::directory?(rubymail_lib) $LOAD_PATH.unshift(rubymail_lib) puts "Prepended #{rubymail_lib} to $LOAD_PATH" end end begin fail "Need to see a lib/rubyfilter dir in current dir" unless FileTest::directory?('lib/rfilter') $LOAD_PATH.unshift("lib") puts "Prepended lib to $LOAD_PATH" end module RFilter module Test class TestBase < RUNIT::TestCase include Config attr_reader :scratch_dir # NoMethodError was introduced in ruby 1.7 NO_METHOD_ERROR = if RUBY_VERSION >= "1.7" NoMethodError else NameError end # Return the elements of $LOAD_PATH that were added with -I or # RUBYLIB. def extra_load_paths extras = $LOAD_PATH.dup [ 'sitedir', 'sitelibdir', 'sitearchdir', 'rubylibdir', 'archdir' ].each { |var| extras.delete(Config::CONFIG[var]) { raise } } extras.delete('.') extras end # Print a string to a temporary file and return the file opened. # This lets you have some test data in a string, but access it with # a file. def string_as_file(string, strip_whitespace = true) if strip_whitespace temp = "" string.each_line { |line| temp += line.sub(/^[ \t]+/, '') } string = temp end file = Tempfile.new("ruby.string_as_file.") begin file.print(string) file.close() file.open() yield file ensure file.close(true) end end # Return true if the given file contains a line matching regexp def file_contains(filename, pattern) unless pattern.kind_of?(Regexp) pattern = Regexp.new(pattern) end detected = nil File.open(filename) { |f| detected = f.detect { |line| line =~ pattern } } ! detected.nil? end # Deletes everything in directory +dir+, including any # subdirectories def cleandir(dir) if FileTest.directory?(dir) files = [] Find.find(dir) { |f| files.push(f) } files.shift # get rid of 'dir' files.reverse_each { |f| if FileTest.directory?(f) Dir.delete(f) else File.delete(f) end } end end def setup name = name().gsub(/[^\w]/, '_') @scratch_dir = File.join(Dir.getwd, "_scratch_" + name) @data_dir = File.join(Dir.getwd, "tests", "data") @scratch_hash = {} cleandir(@scratch_dir) Dir.rmdir(@scratch_dir) if FileTest.directory?(@scratch_dir) Dir.mkdir(@scratch_dir) unless FileTest.directory?(@scratch_dir) end def ruby_program File.join(CONFIG['bindir'], CONFIG['ruby_install_name']) end def data_filename(name) File.join(@data_dir, name) end def data_as_file(name) File.open(data_filename(name)) { |f| yield f } end def scratch_filename(name) if @scratch_hash.key?(name) temp = @scratch_hash[name] temp = temp.succ @scratch_hash[name] = name = temp else temp = name.dup temp << '.0' unless temp =~ /\.\d+$/ @scratch_hash[name] = temp end File.join(@scratch_dir, name) end def teardown unless $! || ((defined? passed?) && !passed?) cleandir(@scratch_dir) Dir.rmdir(@scratch_dir) if FileTest.directory?(@scratch_dir) end end def call_fails(arg, &block) begin yield arg rescue Exception return true end return false end # if a random string failes, run it through this function to find the # shortest fail case def find_shortest_failure(str, &block) unless call_fails(str, &block) raise "hey, the input didn't fail!" else # Chop off stuff from the beginning and then the end # until it stops failing bad = str 0.upto(bad.length) {|index| bad.length.downto(1) {|length| begin loop { s = bad.dup s[index,length] = '' break if bad == s break unless call_fails(s, &block) bad = s } rescue IndexError break end } } raise "shortest failure is #{bad.inspect}" end end end end end rubyfilter-0.12.orig/README0000444001013300101330000000512207773224251017045 0ustar yaegashiyaegashi00000000000000= RubyFilter This is a framework for filtering mail, possibly modifying it, and delivering it to various mailbox formats. RubyFilter is available at: http://www.lickey.com/rubyfilter/ RubyFilter depends on RubyMail, available at: http://www.lickey.com/rubymail/ = Why? The world needs alternatives to procmail. I wanted one that allowed me to write a mail filter in a fully capable scripting language. = Status This package is currently very raw. All API is subject to change. I very much appreciate suggestions and comments. However, I do use this for all of my own mail filtering. = Requirements Ruby 1.6.* or Ruby 1.8.*. Only tested under Linux, should be fine under any Unix. = Documentation See the doc/ subdirectory for HTML documentation. = Install Type the following while in the package directory: ruby install.rb config ruby install.rb setup ruby install.rb install You may need special permissions to execute the last line. If you want to just install RubyMail to a custom location, just copy the rmail subdirectory manually. = Tests? This package has a complete unit test suite (requires RubyUnit to run). = License Copyright (c) 2003 Matt Armstrong. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. The name of the author may not be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. = Support To reach the author of RubyFilter, send mail to matt@lickey.com. rubyfilter-0.12.orig/THANKS0000444001013300101330000000070307773224251017100 0ustar yaegashiyaegashi00000000000000Thanks to Mail::Audit for: - Showing that reasonable mail filters can be written in a scripting language. Thanks to TMDA for: - Showing that an aggressive SPAM heuristic coupled with a confirmation system is an effective technique for reducing SPAM. - The idea of tacking on an HMAC to address extensions (Mail::AddressTagger). Joakim Andersson - Initial implementation of the filter method. Pavel Kolar - but report (1.8.0 incompatability) rubyfilter-0.12.orig/install.rb0000444001013300101330000005301007773224251020157 0ustar yaegashiyaegashi00000000000000# # This file is automatically generated. DO NOT MODIFY! # # install.rb # # Copyright (c) 2000-2002 Minero Aoki # # This program is free software. # You can distribute/modify this program under the terms of # the GNU Lesser General Public License version 2. # ### begin compat.rb unless Enumerable.instance_methods.include? 'inject' module Enumerable def inject( result ) each do |i| result = yield(result, i) end result end end end def File.read_all( fname ) File.open(fname, 'rb') {|f| return f.read } end def File.write( fname, str ) File.open(fname, 'wb') {|f| f.write str } end ### end compat.rb ### begin config.rb if i = ARGV.index(/\A--rbconfig=/) file = $' ARGV.delete_at(i) require file else require 'rbconfig' end class ConfigTable c = ::Config::CONFIG rubypath = c['bindir'] + '/' + c['ruby_install_name'] major = c['MAJOR'].to_i minor = c['MINOR'].to_i teeny = c['TEENY'].to_i version = "#{major}.#{minor}" # ruby ver. >= 1.4.4? newpath_p = ((major >= 2) or ((major == 1) and ((minor >= 5) or ((minor == 4) and (teeny >= 4))))) re = Regexp.new('\A' + Regexp.quote(c['prefix'])) subprefix = lambda {|path| re === path and path.sub(re, '$prefix') } if c['rubylibdir'] # V < 1.6.3 stdruby = subprefix.call(c['rubylibdir']) siteruby = subprefix.call(c['sitedir']) versite = subprefix.call(c['sitelibdir']) sodir = subprefix.call(c['sitearchdir']) elsif newpath_p # 1.4.4 <= V <= 1.6.3 stdruby = "$prefix/lib/ruby/#{version}" siteruby = subprefix.call(c['sitedir']) versite = siteruby + '/' + version sodir = "$site-ruby/#{c['arch']}" else # V < 1.4.4 stdruby = "$prefix/lib/ruby/#{version}" siteruby = "$prefix/lib/ruby/#{version}/site_ruby" versite = siteruby sodir = "$site-ruby/#{c['arch']}" end DESCRIPTER = [ [ 'prefix', [ c['prefix'], 'path', 'path prefix of target environment' ] ], [ 'std-ruby', [ stdruby, 'path', 'the directory for standard ruby libraries' ] ], [ 'site-ruby-common', [ siteruby, 'path', 'the directory for version-independent non-standard ruby libraries' ] ], [ 'site-ruby', [ versite, 'path', 'the directory for non-standard ruby libraries' ] ], [ 'bin-dir', [ '$prefix/bin', 'path', 'the directory for commands' ] ], [ 'rb-dir', [ '$site-ruby', 'path', 'the directory for ruby scripts' ] ], [ 'so-dir', [ sodir, 'path', 'the directory for ruby extentions' ] ], [ 'data-dir', [ '$prefix/share', 'path', 'the directory for shared data' ] ], [ 'ruby-path', [ rubypath, 'path', 'path to set to #! line' ] ], [ 'ruby-prog', [ rubypath, 'name', 'the ruby program using for installation' ] ], [ 'make-prog', [ 'make', 'name', 'the make program to compile ruby extentions' ] ], [ 'without-ext', [ 'no', 'yes/no', 'does not compile/install ruby extentions' ] ] ] SAVE_FILE = 'config.save' def ConfigTable.each_name( &block ) keys().each(&block) end def ConfigTable.keys DESCRIPTER.collect {|k,*dummy| k } end def ConfigTable.each_definition( &block ) DESCRIPTER.each(&block) end def ConfigTable.get_entry( name ) name, ent = DESCRIPTER.assoc(name) ent end def ConfigTable.get_entry!( name ) get_entry(name) or raise ArgumentError, "no such config: #{name}" end def ConfigTable.add_entry( name, vals ) ConfigTable::DESCRIPTER.push [name,vals] end def ConfigTable.remove_entry( name ) get_entry name or raise ArgumentError, "no such config: #{name}" DESCRIPTER.delete_if {|n,arr| n == name } end def ConfigTable.config_key?( name ) get_entry(name) ? true : false end def ConfigTable.bool_config?( name ) ent = get_entry(name) or return false ent[1] == 'yes/no' end def ConfigTable.value_config?( name ) ent = get_entry(name) or return false ent[1] != 'yes/no' end def ConfigTable.path_config?( name ) ent = get_entry(name) or return false ent[1] == 'path' end class << self alias newobj new def new c = newobj() c.__send__ :init c end def load c = newobj() raise InstallError, "#{File.basename $0} config first"\ unless File.file? SAVE_FILE File.foreach(SAVE_FILE) do |line| k, v = line.split(/=/, 2) c.instance_eval { @table[k] = v.strip } end c end end def initialize @table = {} end def init DESCRIPTER.each do |k, (default, vname, desc, default2)| @table[k] = default end end private :init def save File.open(SAVE_FILE, 'w') {|f| @table.each do |k, v| f.printf "%s=%s\n", k, v if v end } end def []=( k, v ) ConfigTable.config_key? k or raise InstallError, "unknown config option #{k}" if ConfigTable.path_config? k @table[k] = (v[0,1] != '$') ? File.expand_path(v) : v else @table[k] = v end end def []( key ) @table[key] or return nil @table[key].gsub(%r<\$([^/]+)>) { self[$1] } end def set_raw( key, val ) @table[key] = val end def get_raw( key ) @table[key] end end class MetaConfigEnvironment def self.eval_file( file ) return unless File.file? file new.instance_eval File.read_all(file), file, 1 end private def config_names ConfigTable.keys end def config?( name ) ConfigTable.config_key? name end def bool_config?( name ) ConfigTable.bool_config? name end def value_config?( name ) ConfigTable.value_config? name end def path_config?( name ) ConfigTable.path_config? name end def add_config( name, argname, default, desc ) ConfigTable.add_entry name,[default,argname,desc] end def add_path_config( name, default, desc ) add_config name, 'path', default, desc end def add_bool_config( name, default, desc ) add_config name, 'yes/no', default ? 'yes' : 'no', desc end def set_config_default( name, default ) if bool_config? name ConfigTable.get_entry!(name)[0] = default ? 'yes' : 'no' else ConfigTable.get_entry!(name)[0] = default end end def remove_config( name ) ent = ConfigTable.get_entry(name) ConfigTable.remove_entry name ent end end ### end config.rb ### begin fileop.rb module FileOperations def mkdir_p( dname, prefix = nil ) dname = prefix + dname if prefix $stderr.puts "mkdir -p #{dname}" if verbose? return if no_harm? # does not check '/'... it's too abnormal case dirs = dname.split(%r<(?=/)>) if /\A[a-z]:\z/i === dirs[0] disk = dirs.shift dirs[0] = disk + dirs[0] end dirs.each_index do |idx| path = dirs[0..idx].join('') Dir.mkdir path unless dir? path end end def rm_f( fname ) $stderr.puts "rm -f #{fname}" if verbose? return if no_harm? if File.exist? fname or File.symlink? fname File.chmod 0777, fname File.unlink fname end end def rm_rf( dn ) $stderr.puts "rm -rf #{dn}" if verbose? return if no_harm? Dir.chdir dn Dir.foreach('.') do |fn| next if fn == '.' next if fn == '..' if dir? fn verbose_off { rm_rf fn } else verbose_off { rm_f fn } end end Dir.chdir '..' Dir.rmdir dn end def mv( src, dest ) rm_f dest begin File.link src, dest rescue File.write dest, File.read_all(src) File.chmod File.stat(src).mode, dest end rm_f src end def install( from, dest, mode, prefix = nil ) $stderr.puts "install #{from} #{dest}" if verbose? return if no_harm? realdest = prefix + dest if prefix if dir? realdest realdest += '/' + File.basename(from) end str = File.read_all(from) if diff? str, realdest verbose_off { rm_f realdest if File.exist? realdest } File.write realdest, str File.chmod mode, realdest File.open(objdir + '/InstalledFiles', 'a') {|f| f.puts realdest } end end def diff?( orig, targ ) return true unless File.exist? targ orig != File.read_all(targ) end def command( str ) $stderr.puts str if verbose? system str or raise RuntimeError, "'system #{str}' failed" end def ruby( str ) command config('ruby-prog') + ' ' + str end def dir?( dname ) # for corrupted windows stat() File.directory?((dname[-1,1] == '/') ? dname : dname + '/') end def all_files( dname ) Dir.open(dname) {|d| return d.find_all {|n| File.file? "#{dname}/#{n}" } } end def all_dirs( dname ) Dir.open(dname) {|d| return d.find_all {|n| dir? "#{dname}/#{n}" } - %w(. ..) } end end ### end fileop.rb ### begin base.rb class InstallError < StandardError; end class Installer Version = '3.1.3' Copyright = 'Copyright (c) 2000-2002 Minero Aoki' @toplevel = nil def self.declear_toplevel_installer( inst ) @toplevel and raise ArgumentError, 'more than one toplevel installer decleared' @toplevel = inst end def self.toplevel_installer @toplevel end FILETYPES = %w( bin lib ext data ) include FileOperations def initialize( config, opt, srcroot, objroot ) @config = config @options = opt @srcdir = File.expand_path(srcroot) @objdir = File.expand_path(objroot) @currdir = '.' end def inspect "#<#{self.class} #{__id__}>" end # # configs/options # def get_config( key ) @config[key] end alias config get_config def set_config( key, val ) @config[key] = val end def no_harm? @options['no-harm'] end def verbose? @options['verbose'] end def verbose_off save, @options['verbose'] = @options['verbose'], false yield @options['verbose'] = save end # # srcdir/objdir # attr_reader :srcdir alias srcdir_root srcdir alias package_root srcdir def curr_srcdir "#{@srcdir}/#{@currdir}" end attr_reader :objdir alias objdir_root objdir def curr_objdir "#{@objdir}/#{@currdir}" end def srcfile( path ) curr_srcdir + '/' + path end def srcexist?( path ) File.exist? srcfile(path) end def srcdirectory?( path ) dir? srcfile(path) end def srcfile?( path ) File.file? srcfile(path) end def srcentries( path = '.' ) Dir.open(curr_srcdir + '/' + path) {|d| return d.to_a - %w(. ..) - hookfilenames } end def srcfiles( path = '.' ) srcentries(path).find_all {|fname| File.file? File.join(curr_srcdir, path, fname) } end def srcdirectories( path = '.' ) srcentries(path).find_all {|fname| dir? File.join(curr_srcdir, path, fname) } end def dive_into( rel ) return unless dir? "#{@srcdir}/#{rel}" return if File.basename(rel) == "SCCS" dir = File.basename(rel) Dir.mkdir dir unless dir? dir save = Dir.pwd Dir.chdir dir $stderr.puts '---> ' + rel if verbose? @currdir = rel yield Dir.chdir save $stderr.puts '<--- ' + rel if verbose? @currdir = File.dirname(rel) end # # config # def exec_config exec_task_traverse 'config' end def config_dir_bin( rel ) end def config_dir_lib( rel ) end def config_dir_ext( rel ) extconf if extdir? curr_srcdir end def extconf opt = @options['config-opt'].join(' ') command "#{config('ruby-prog')} #{curr_srcdir}/extconf.rb #{opt}" end def config_dir_data( rel ) end # # setup # def exec_setup exec_task_traverse 'setup' end def setup_dir_bin( relpath ) all_files(curr_srcdir).each do |fname| add_rubypath "#{curr_srcdir}/#{fname}" end end SHEBANG_RE = /\A\#!\s*\S*ruby\S*/ def add_rubypath( path ) $stderr.puts %Q if verbose? return if no_harm? tmpfile = File.basename(path) + '.tmp' begin File.open(path) {|r| File.open(tmpfile, 'w') {|w| first = r.gets return unless SHEBANG_RE === first # reject '/usr/bin/env ruby' w.print first.sub(SHEBANG_RE, '#!' + config('ruby-path')) w.write r.read } } mv tmpfile, File.basename(path) ensure rm_f tmpfile if File.exist? tmpfile end end def setup_dir_lib( relpath ) end def setup_dir_ext( relpath ) make if extdir?(curr_srcdir) end def make command config('make-prog') end def setup_dir_data( relpath ) end # # install # def exec_install exec_task_traverse 'install' end def install_dir_bin( rel ) install_files targfiles, config('bin-dir') + '/' + rel, 0755 end def install_dir_lib( rel ) install_files targfiles, config('rb-dir') + '/' + rel, 0644 end def install_dir_ext( rel ) install_dir_ext_main File.dirname(rel) if extdir?(curr_srcdir) end def install_dir_ext_main( rel ) install_files allext('.'), config('so-dir') + '/' + rel, 0555 end def install_dir_data( rel ) install_files targfiles, config('data-dir') + '/' + rel, 0644 end def install_files( list, dest, mode ) mkdir_p dest, @options['install-prefix'] list.each do |fname| install fname, dest, mode, @options['install-prefix'] end end def targfiles (targfilenames() - hookfilenames()).collect {|fname| File.exist?(fname) ? fname : File.join(curr_srcdir(), fname) } end def targfilenames [ curr_srcdir(), '.' ].inject([]) {|ret, dir| ret | all_files(dir) } end def hookfilenames %w( pre-%s post-%s pre-%s.rb post-%s.rb ).collect {|fmt| %w( config setup install clean ).collect {|t| sprintf fmt, t } }.flatten end def allext( dir ) _allext(dir) or raise InstallError, "no extention exists: Have you done 'ruby #{$0} setup' ?" end DLEXT = /\.#{ ::Config::CONFIG['DLEXT'] }\z/ def _allext( dir ) Dir.open(dir) {|d| return d.find_all {|fname| DLEXT === fname } } end # # clean # def exec_clean exec_task_traverse 'clean' rm_f 'config.save' rm_f 'InstalledFiles' end def clean_dir_bin( rel ) end def clean_dir_lib( rel ) end def clean_dir_ext( rel ) clean end def clean command config('make-prog') + ' clean' if File.file? 'Makefile' end def clean_dir_data( rel ) end # # lib # def exec_task_traverse( task ) run_hook 'pre-' + task FILETYPES.each do |type| if config('without-ext') == 'yes' and type == 'ext' $stderr.puts 'skipping ext/* by user option' if verbose? next end traverse task, type, task + '_dir_' + type end run_hook 'post-' + task end def traverse( task, rel, mid ) dive_into(rel) { run_hook 'pre-' + task __send__ mid, rel.sub(%r_\A.*?(?:/|\z)_, '') all_dirs(curr_srcdir).each do |d| traverse task, rel + '/' + d, mid end run_hook 'post-' + task } end def run_hook( name ) try_run_hook curr_srcdir + '/' + name or try_run_hook curr_srcdir + '/' + name + '.rb' end def try_run_hook( fname ) return false unless File.file? fname env = self.dup begin env.instance_eval File.read_all(fname), fname, 1 rescue raise InstallError, "hook #{fname} failed:\n" + $!.message end true end def extdir?( dir ) File.exist? dir + '/MANIFEST' end end ### end base.rb ### begin toplevel.rb class ToplevelInstaller < Installer TASKS = [ [ 'config', 'saves your configurations' ], [ 'show', 'shows current configuration' ], [ 'setup', 'compiles extention or else' ], [ 'install', 'installs files' ], [ 'clean', "does `make clean' for each extention" ] ] def initialize( root ) super nil, {'verbose' => true}, root, '.' Installer.declear_toplevel_installer self end def execute run_metaconfigs case task = parsearg_global() when 'config' @config = ConfigTable.new else @config = ConfigTable.load end parsearg_TASK task exectask task end def run_metaconfigs MetaConfigEnvironment.eval_file "#{srcdir_root()}/#{metaconfig()}" end def metaconfig 'metaconfig' end def exectask( task ) if task == 'show' exec_show else try task end end def try( task ) $stderr.printf "#{File.basename $0}: entering %s phase...\n", task if verbose? begin __send__ 'exec_' + task rescue $stderr.printf "%s failed\n", task raise end $stderr.printf "#{File.basename $0}: %s done.\n", task if verbose? end # # processing arguments # def parsearg_global task_re = /\A(?:#{TASKS.collect {|i| i[0] }.join '|'})\z/ while arg = ARGV.shift case arg when /\A\w+\z/ task_re === arg or raise InstallError, "wrong task: #{arg}" return arg when '-q', '--quiet' @options['verbose'] = false when '--verbose' @options['verbose'] = true when '-h', '--help' print_usage $stdout exit 0 when '-v', '--version' puts "#{File.basename $0} version #{Version}" exit 0 when '--copyright' puts Copyright exit 0 else raise InstallError, "unknown global option '#{arg}'" end end raise InstallError, "No task or global option given. Typical installation procedure is: $ ruby #{File.basename $0} config $ ruby #{File.basename $0} setup # ruby #{File.basename $0} install (may require root privilege) " end def parsearg_TASK( task ) mid = "parsearg_#{task}" if respond_to? mid, true __send__ mid else ARGV.empty? or raise InstallError, "#{task}: unknown options: #{ARGV.join ' '}" end end def parsearg_config re = /\A--(#{ConfigTable.keys.join '|'})(?:=(.*))?\z/ @options['config-opt'] = [] while i = ARGV.shift if /\A--?\z/ === i @options['config-opt'] = ARGV.dup break end m = re.match(i) or raise InstallError, "config: unknown option #{i}" name, value = m.to_a[1,2] if value if ConfigTable.bool_config?(name) /\A(y(es)?|n(o)?|t(rue)?|f(alse))\z/i === value or raise InstallError, "config: --#{name} allows only yes/no for argument" value = (/\Ay(es)?|\At(rue)/i === value) ? 'yes' : 'no' end else ConfigTable.bool_config?(name) or raise InstallError, "config: --#{name} requires argument" value = 'yes' end @config[name] = value end end def parsearg_install @options['no-harm'] = false @options['install-prefix'] = '' while a = ARGV.shift case a when /\A--no-harm\z/ @options['no-harm'] = true when /\A--prefix=(.*)\z/ path = $1 path = File.expand_path(path) unless path[0,1] == '/' @options['install-prefix'] = path else raise InstallError, "install: unknown option #{a}" end end end def print_usage( out ) out.puts 'Typical Installation Procedure:' out.puts " $ ruby #{File.basename $0} config" out.puts " $ ruby #{File.basename $0} setup" out.puts " # ruby #{File.basename $0} install (may require root privilege)" out.puts out.puts 'Detailed Usage:' out.puts " ruby #{File.basename $0} " out.puts " ruby #{File.basename $0} [] []" fmt = " %-20s %s\n" out.puts out.puts 'Global options:' out.printf fmt, '-q,--quiet', 'suppress message outputs' out.printf fmt, ' --verbose', 'output messages verbosely' out.printf fmt, '-h,--help', 'print this message' out.printf fmt, '-v,--version', 'print version and quit' out.printf fmt, ' --copyright', 'print copyright and quit' out.puts out.puts 'Tasks:' TASKS.each do |name, desc| out.printf " %-10s %s\n", name, desc end out.puts out.puts 'Options for config:' ConfigTable.each_definition do |name, (default, arg, desc, default2)| out.printf " %-20s %s [%s]\n", '--'+ name + (ConfigTable.bool_config?(name) ? '' : '='+arg), desc, default2 || default end out.printf " %-20s %s [%s]\n", '--rbconfig=path', 'your rbconfig.rb to load', "running ruby's" out.puts out.puts 'Options for install:' out.printf " %-20s %s [%s]\n", '--no-harm', 'only display what to do if given', 'off' out.printf " %-20s %s [%s]\n", '--prefix', 'install path prefix', '$prefix' out.puts end # # config # def exec_config super @config.save end # # show # def exec_show ConfigTable.each_name do |k| v = @config.get_raw(k) if not v or v.empty? v = '(not specified)' end printf "%-10s %s\n", k, v end end end ### end toplevel.rb if $0 == __FILE__ begin installer = ToplevelInstaller.new(File.dirname($0)) installer.execute rescue raise if $DEBUG $stderr.puts $!.message $stderr.puts "Try 'ruby #{$0} --help' for detailed usage." exit 1 end end