From: Yasunari Momoi Date: 1997-09-08T18:31:54+09:00 Subject: [ruby-list:4201] Mini ML Program ----Next_Part(Mon_Sep__8_18:37:42_1997_809)-- Content-Type: Text/Plain; charset=iso-2022-jp Content-Transfer-Encoding: 7bit ももちゃん@あるもにこすです. とりあえず ruby 入門ということで,最も単純な ML のプログラムを 書いてみました. 本当はもっとちゃんと機能を付けてから出したかったんですが, ・次に触るのがいつになるか分からない ・まずそうな所はとっとと指摘された方が矯正が楽 :-) というのがあるので,とりあえず送ってみます. より美しいスタイル,的確な例外処理 (今はやってない) をどういう 風に入れると頑丈になるか,など,いろいろ教えて頂けると嬉しいで す (_ _). -- 最終的には,ML や procmail みたいなものを作るための toolkit に なる部品郡を作ろうと思ってます. # 最初は perl で書こうと思ってたんだけど,perl で OOP しようと # してごちゃごちゃになって,嫌になってやめた. 今後欲しい/作るもの: libSMTP libMIME libMailHeader とりあえず libMailHeader から作ろう... ----Next_Part(Mon_Sep__8_18:37:42_1997_809)-- Content-Type: Text/Plain; charset=iso-2022-jp Content-Transfer-Encoding: 7bit class CMail # コンストラクタ def CMail.new(f) unless f.kind_of?(IO) f = open(f, "r") me = super(f) # call initialize(f) f.close else me = super end return me end # 初期化 def initialize(f) @header = [] @body = [] while f.gets() $_.chop! next if /^From / # skip From-line break if /^$/ # end of header if /^(\S+):\s*(.*)/ attr = $1 @header << $1 + ": " + $2 + "\n" else sub!(/^\s+/, '') @header << @header.pop + "\t" + $_ + "\n" end end return unless $_ while f.gets() break if /^From / @body.push($_) end end # メールヘッダを取り出す def header return @header end # メールボディを取り出す def body return @body end # 指定ヘッダフィールドを全て取り出す def [](field) f = [] @header.each { |l| f << l if l =~ /^#{field}: /i } return f end # ヘッダフィールドの継続行をつなげて一行にする def join_header header = [] @header.each { |l| line = l.clone line.gsub!(/\r/, '') line.gsub!(/\n/, '') line.gsub!(/\t/, ' ') if line =~ /^\s+(.*)/ line = header.pop + " " + $1 else if not header.empty? header << header.pop + "\n" end end header << line } header << header.pop + "\n" return header end end ----Next_Part(Mon_Sep__8_18:37:42_1997_809)-- Content-Type: Text/Plain; charset=iso-2022-jp Content-Transfer-Encoding: 7bit class CMailSpool MAX_FILES = 1000 # 1つのディレクトリ以下の最大ファイル数 # コンストラクタ def CMailSpool.new(d) return super(d) end # 初期化 def initialize(d) @dir = d end # ディレクトリを作る def create_dir(dir) Dir.mkdir(dir) if not File.exist?(dir) end private :create_dir # ファイルを入れるべき場所を返す def where_to_go(article) return @dir if MAX_FILES <= 0 base = (article / MAX_FILES).to_i * MAX_FILES return @dir + "/" + base.to_s end private :where_to_go # 現在ある sequence ファイルの中で最大のモノを取り出す def last_sequence() dirs = Dir.glob(@dir + "/*") max = 0 dirs.each { |l| next if File.directory?(l) i = File.basename(l).to_i max = i if max < i } dir = @dir + "/" + max.to_s files = Dir.glob(dir + "/*") files.each { |l| i = File.basename(l).to_i max = i if max < i } return max end private :last_sequence # sequence 番のファイルに body を書く def put(seq, body) dir = where_to_go(seq) create_dir(dir) f = File.new(dir + "/" + seq.to_s, "w") f << body f.close end # 最大の sequence を持つファイルを追加する def add(body) print last_sequence, "\n" put(last_sequence + 1, body) end # sequence 番のファイルを get する def get(seq) fn = where_to_go(seq) + "/" + seq.to_s return [] if not File.exist?(fn) f = File.new(fn, "r") body = [] f.each { |l| body << l } f.close return body end end ----Next_Part(Mon_Sep__8_18:37:42_1997_809)-- Content-Type: Text/Plain; charset=iso-2022-jp Content-Transfer-Encoding: 7bit #!/usr/local/bin/ruby ########## # $ml_address = 'hoge-ml@hoge.co.jp' $ml_admin = 'owner-hoge-ml@hoge.co.jp' $ml_member = './members' $ml_spool = './spool' $sendmail = '/usr/lib/sendmail' ########## # メンバリストを作る def get_members(file) f = File.open(file) members = "" f.each_line { |l| line = l.clone line.sub!(/#.*$/, '') if /(\S+@\S+)/ members += $1 + " " end } f.close return members end ########## # ヘッダを作る ## Reply-To を付ける.元のメールにあったらそれを get. ## いらないフィールドは削る.X-* は残す? def create_header(header) field_proc = { "from:" => Proc.new { |l| l }, "to:" => Proc.new { |l| l }, "cc:" => Proc.new { |l| l }, "mime-version:" => Proc.new { |l| l }, "content-type:" => Proc.new { |l| l }, "content-transfer-encoding:" => Proc.new { |l| l }, "reply-to:" => Proc.new { |l| reply_flag = TRUE l }, "subject:" => Proc.new { |l| s = "" if l =~ /^subject:\s*(.*)/i s = $1 end s.gsub(/re:\s*re:/i, "Re:") "Subject: " + s + "\n" }, } target = [] reply_flag = FALSE header.each { |l| f = l.split[0].downcase if field_proc.key?(f) target << field_proc[f].call(l) end } if reply_flag == FALSE target << "Reply-To: " + $ml_address + "\n" end return target end ########## # main require "CMail" orig = CMail.new(STDIN) head = create_header(orig.join_header) body = orig.body mail = head + "\n" + body print mail # メンバリストを取得 member = get_members($ml_member) print member, "\n" exit 0 # メールを送る com = "|" + $sendmail + " -oi -f" + $ml_admin + " " + member sendmail = open(com, "w") sendmail.write(mail + "\n") sendmail.close # spool に保存 require "CMailSpool" s = CMailSpool.new($ml_spool) s.add(mail) exit 0 ----Next_Part(Mon_Sep__8_18:37:42_1997_809)----