LaterMail 再修正

昨日、今日とまたいくつかのバグに対応しました。

ヘッダの順番依存

Message-Id: ヘッダの前に From: や To: などの重要なヘッダが来るものだと思い込んで作っていたのですが、そういう前提はまったくないようです。Message-Id: ヘッダがかなり最初の方に出てきて、そのあと、From: や To: が続くメールもたくさんあるようです。ですから、どの順序で出てきても大丈夫なようにプログラムを修正しました。

CC:、BCC:

CC: と BCC: については特に処理をしないでそのまま配達用のファイルに書いていたのですが、CC: や BCC: が書かれていると、sendmail がそのファイルを読んで処理するときに、CC: や BCC: 宛てにもメールを送ってしまいます。(当然ですね)
もし、"CC: 1@5-55.jp" となっていると、再配達したメールを送り先(root@5-55.jp です)へ1時間後に再配達することになり、延々と root@5-55.jp にメールが届くような事態になってしまいます。
ですので、CC: と BCC: についてはファイルに書かない(再配達しない)ようにしました。

latermail.rb

たぶんこれで最後だと思いますので、最新ソースを貼り付けておきます。

require 'time'
require 'nkf'
$KCODE = 'utf-8'

class LaterMail
  # ファイル名(時刻+Message-Id)が決まるまで、@bufに溜めておく
  def initialize(mdir = '')
    @dir = mdir
    @buf = Array.new
    @io  = nil
  end

  # 0 - 24              -->    指定時間をプラス
  # sunday - saturday    -->    次の指定曜日までの日数をプラス(曜日以外は入ってこないが、入ってきてもプラスしないだけ)
  def add(dt, to)
    week = { 'sunday' => 0, 'monday' => 1, 'tuesday' => 2, 'wednesday' => 3, 'thursday' => 4, 'friday' => 5, 'saturday' => 6 }

    t = Time.parse(NKF.nkf('-Jw -xm0', dt).gsub(/[年月日]/,'/'))
    if to
      if to =~ /\d/
        t += to.to_i * 3600
      elsif week[to]
        t += ]/).each { |wd| return wd if wd.include?('@') }
    nil
  end
  
  def putbuf(line)
    if @io
      @io.puts line
    else
      @buf << line
    end
  end
        
  def write
    ct = 0
    from = to = boundary = dt = nil
    from_write = to_write = tof = ccf = false
    while line = gets
      if boundary && line.include?("--#{boundary}")
        if (ct += 1) == 2
          putbuf(commercial(to">*1
        end
      end
      if (mail = get_mail(line))
        user = mail.split('@')[0]
      end
      case line
      when /^From /i, /^Return-Path:/i, /^Sender:/i
        from = mail
        putbuf(line)
      when /^X-Original-To:/i
        to = user
        putbuf("X-Original-To: #{from}")
      when /for\s*<.*@5-55\.jp>/i
        to = user
        putbuf(line)
      when /^Delivered-To:/i
        to = user                         # sendmailがINSERTするので書かない
      when /^Date:/i
        dt = add(line, to)
        putbuf("Date: #{dt.to_s}")
      when /^From:/i
        from_write = true
        putbuf(from_message)
      when /^To:/i
        to_write, tof, ccf = true, true, false
        putbuf("To: #{from}")
      when /^Cc:/i, /^Bcc:/i
        ccf, tof = true, false            # Cc: Bcc: は処理しない
      when /^Content-Type: multipart/i
        boundary = line.split('"')[1]
        putbuf(line)
      when /^Message-Id:/i
        putbuf(from_message)    if !from_write && from
        putbuf("To: #{from}")  if !to_write
        putbuf(line)
        dt = add(Time.now.to_s, to)    unless dt
        @io = open("#{@dir}#{dt.strftime('%Y%m%d%H%M%S')}.#{user}", "w")
        @buf.each { |bf| putbuf(bf) }
      else
        if tof || ccf
          next  if line =~ /^\s/
          tof = ccf = false
        end
        putbuf(line)
      end
    end
    unless boundary
      putbuf(commercial(to))
    end
  end
end
if __FILE__ == $0
# cat mail > ruby latermail.rb
  lmail = LaterMail.new("/home/hml/Maildir/tmp/")
  lmail.write
end


Creative Commons License

*1:6 - t.wday + week[to]) % 7 + 1) * 3600 * 24 t = Time.mktime(t.year, t.month, t.day, 7) end end t end def from_message "From: LaterMail " end def ago(to) if !to || to == '0' "reply immediately" elsif to =~ /\d/ "#{to} hours before replying" else "today is #{to}" end end def commercial(to) "\n--------------------------------------------\n" + "#{ago(to)} by http://lm.5-55.jp\n" + "and please try http://2.5-55.jp (#{NKF.nkf('-jW -xm0', '通販Go!Go!')})\n\n" end def get_mail(line) line.split(/[\s<>]/).each { |wd| return wd if wd.include?('@') } nil end def putbuf(line) if @io @io.puts line else @buf << line end end def write ct = 0 from = to = boundary = dt = nil from_write = to_write = tof = ccf = false while line = gets if boundary && line.include?("--#{boundary}") if (ct += 1) == 2 putbuf(commercial(to