FreeBSDで運用する(その12)
随分と久しぶりだ。
諸事情があって若干田舎に引っ越したため、光ケーブルの引き込みに2ヶ月弱掛かってしまった。
お陰で、自分と家族は「インターネットから切り離されて生きることが難しいことを自覚する」と言う貴重な体験をした。
前回は、会社内外から送信された影舞レポートメールを影舞ユーザで受信し、内容が正しければwwwユーザに転送すると言う説明をした。
影舞ユーザの登録はrootユーザで次のように行う。
# adduser Username: kagemai Full name: Bug Tracking System Kagemai Uid (Leave empty for default): Login group [kagemai]: Login group is kagemai. Invite kagemai into other groups? []: Login class [default]: Shell (sh csh tcsh bash fdsh nologin) [sh]: nologin Home directory [/home/hoge]: /usr/local/www/htdocs/kagemai Use password-based authentication? [yes]: Use an empty password? (yes/no) [no]: Use a random password? (yes/no) [no]: Enter password:***** Enter password again:***** Lock out the account after creation? [no]: Username : kagemai Password : ***** Full Name : Bug Tracking System Kagemai Uid : 1002 Class : Groups : kagemai Home : /usr/local/www/htdocs/kagemai Shell : /usr/sbin/nologin Locked : no OK? (yes/no):y
qmail上で影舞ユーザがメールを受信したときに、指定したプログラムを実行するためには~kagemai/.qmailを次のように記述する。
#↓これはデバッグ用 #|cat >> ~/mail.txt #↓これはプログラム実行用 |ruby ~/mailtrn.rb
そして、影舞ユーザがメールを受信したときに実行するrubyスクリプトの~kagemai/mailtrn.rbを以下のように記述する。
#!/usr/bin/env ruby =begin このスクリプトは影舞をインストールしたディレクトリ直下に置くこと。 kagemai@DOMAIN.co.jp → kagemai@KAGEMAI.LOCALDOMAIN.co.jp に送信 されたメールを www@KAGEMAI.LOCALDOMAIN.co.jp に転送し、そちらで 影舞へのバグ報告を投稿する。 これは、CGIの実効ユーザである www が正常に読み書きできるようにす るために、影舞ディレクトリの所有者を www に設定しており、影舞への バグ報告メールの受け手である kagemai では、直接書き込めないファイ ルやディレクトリが存在するからである。 =end $kagemai_root = File.dirname(File.expand_path(__FILE__)).untaint # setup config_file = "#{$kagemai_root}/html/kagemai.conf" # setup $:.unshift("#{$kagemai_root}/lib") require 'kagemai/config' Kagemai::Config.initialize($kagemai_root, config_file) require 'nkf' require 'base64' require 'kagemai/mail/mailer' $transfer = "www@KAGEMAI.LOCALDOMAIN.co.jp" $magic_guid = "{322A5CD3-8A74-4c2a-A988-18B65C57CE6B}" $recv_email = "kagemai@DOMAIN.co.jp" $my_email = "kagemai@KAGEMAI.LOCALDOMAIN.co.jp" $project_dir = "#{$kagemai_root}/project" =begin strを影舞をインストールしたディレクトリ直下のdebug.txtに出力する。 =end def debug(str) open("#{$kagemai_root}/debug.txt", "a") do |file| file.puts str end end =begin strからメールアドレスを取り出す。 具体的には "name" <name@domain> や name <name@domain> などから name@domain を取り出す。 それ以外のパターンはそのまま返す。 =end def get_email(str) if (str =~ /.*<(.+?)>/) return $1 else return str end end =begin nameに一致するプロジェクトを検索する。 =end def match_project(name) Dir["#{$project_dir}/*"].each do |dir| dir.gsub!("#{$project_dir}/", "") if (name == dir) return true end end return false end =begin メールの To が影舞ユーザか? Subject が "[PROJECT[:<BTS#>]]"(例:"[test:263]")に一致するか? をチェックする。 =end def check_mail(lines) project = "" lines.each do |line| if (line =~ /^To: /) email = get_email($') if (email != $recv_email) return "invalid recipient: #{email}" end elsif (line =~ /^Subject: /) subject = Base64.decode_b($') subject = NKF::nkf("-m0j", subject) if (subject =~ /^.*?\[(.+?)[:\]]/) project = $1 if (!match_project(project)) return "unknown project: #{project}" end else return "subject not include project name: #{subject}" end end end if (project == "") return "mail not include subject" end return "" end =begin WWWユーザにlinesのメールを転送する。 =end def transfer_mail(lines) body = "" body += "Delivered-To: #{$my_email}\n" # $magic_guidでWWWユーザに影舞ユーザから送信していることを通知 body += "X-Kagemai-Mail-Transfer: #{$magic_guid}\n" lines.each do |line| body += "#{line}\n" end smtp = Kagemai::SmtpMailer.new smtp.sendmail(body, $recv_email, $transfer); end =begin lines からメールアドレスを取り出す。 =end def get_ret_email(lines) level = 0 email = "" lines.each do |line| cur_level = 0 cur_email = "" if (line =~ /^Return-Path: /) cur_level = 10 cur_email = get_email($') elsif (line =~ /^From: /) cur_level = 20 cur_email = get_email($') elsif (line =~ /^Reply-To: /) #cur_level = 30 #cur_email = get_email($') return get_email($') elsif (line == "") return email end if (cur_level > level) level = cur_level; email = cur_email; end end return email end =begin エラーメールを通知する。 =end def error_mail(lines, errmsg) email = get_ret_email(lines) if (email == "" || email == $my_email || email == $recv_email) return; end debug("error mail to #{email}\n"); body = "" body += "From: #{$recv_email}\n" body += "To: #{email}\n" body += "Subject: kagemai mail error\n" dup_item = ["Content-Type", "Content-Transfer-Encoding"] lines.each do |line| if (line == "") break else dup_item.each do |item| if (line =~ /^#{item}: /) body += "#{line}\n" end end end end body += "\n" body += "#{errmsg}\n" body += "\n" body += "--- original message ---\n" body += "\n" in_header = true dup_item = ["From", "To"] lines.each do |line| if (in_header) if (line == "") in_header = false body += "\n" elsif (line =~ /^Subject: /) subject = Base64.decode_b($') subject = NKF::nkf("-m0j", subject) body += "Subject: #{subject}\n" else dup_item.each do |item| if (line =~ /^#{item}: /) body += "#{line}\n" end end end else body += "#{line}\n" end end smtp = Kagemai::SmtpMailer.new smtp.sendmail(body, $recv_email, email); end #ここ↓からメイン lines = STDIN.read.split(/\n/) # メールを文字列配列に読み込む errmsg = check_mail(lines); # メールの内容をチェック # debug("check mail: #{errmsg}\n") if (errmsg == "") transfer_mail(lines) # エラーがなかったらWWWユーザに転送 else error_mail(lines, errmsg) # エラーだったらエラー通知を返信 end exit 0
qmailのセキュリティでは、以下のようにファイルのパーミッションを変更して置かないと、正常に動作しないので注意する必要がある。
# cd ~kagemai # chown kagemai:kagemai .qmail mailrcv.rb # chmod 600 .qmail mailrcv.rb
これで、影舞ユーザの設定は完了である。