電子メイルはそのメッセージがファイルとして保存されるだけではなく, メッセージの内容を標準入力としたプログラムの起動も行なえる。
電子メイルは,送信者がクライアントソフトウェアを用いてメッセージを送ると 送信者側のメイルサーバを経て受信者側のメイルサーバに届く(下図参照)。
中継点となるメイルサーバが複数あったり,ない場合もあるが, 最終的には受信者のメイルボックスのあるメイルサーバで, ファイル単位の書き込み処理がされる。
ここでは,MDA(Mail Delivery Agent)が,送られたメッセージをファイルに 書き込む時に自動処理を行なわせる方法を考える。
qmail(7)では,qmail-local(8)プログラムがサーバ上にある
各ユーザ宛のメイルの配送を請け負う。一般ユーザ宛てメッセージの最終配送先は
~user/.qmail
ファイルで決定する。
このファイルは dot-qmail(5) ファイル形式で記述する。1行1エントリで
以下のいずれかを書ける。
行頭文字 | 種類 | 例 |
---|---|---|
# | コメント | # メモ |
| | プログラム | |./program arg |
. または / (スラッシュで終わらない) |
mbox形式のファイル | ./mbox |
. または / (スラッシュで終わる) |
maildir形式のディレクトリ | ./maildir/ |
& | 転送メイルアドレス | &taro@example.ac.jp |
また,dot-qmail ファイルとして
~user/.qmail-ext
があると,
user-ext という宛先の配送先として利用する。
これを拡張アドレスといい,ext の部分を
拡張子という。また,ext を "default" にした
~user/.qmail-default
というファイルを作ると,対応する dot-qmail ファイルがない場合の
デフォルトの宛先となる。つまり,user-abc,
user-xyz, user-hoge, user-foo, ...
など任意の拡張子を持つローカルな宛先に対して,
対応する dot-qmail ファイルがない場合はすべて
~user/.qmail-default
で定義される宛先に配送される。
dot-qmail ファイル中の |
で始まる行は,
すぐ後ろに書いたプログラムを
その記述のとおりに起動する。このとき以下の環境変数が自動的に
設定された状態でプログラムが呼ばれる。
環境変数 | 値の意味 |
---|---|
SENDER | エンベロープsender |
RECIPIENT | 受信者アドレス |
USER | 受信者のユーザ名 |
HOME | 受信者のホームディレクトリ |
HOST | 受信アドレスのドメイン部 |
LOCAL | 受信アドレスのローカル部 |
EXT | 拡張子部分(ユーザ名以降最初のハイフン以降) |
EXT2 | $EXT の1個目のハイフン以降 |
EXT3 | $EXT の2個目のハイフン以降 |
EXT4 | $EXT の3個目のハイフン以降 |
DEFAULT | default でマッチした文字列 |
たとえば,hanako@example.com が taro-foo@example.co.jp
宛てに出したメイルが taro 宛に届く場合を考える。
~taro/.qmail-foo
に
| program args...
のように書いてある場合の program が起動するとき, 環境変数は以下のように設定される。
環境変数 | 値 |
---|---|
SENDER | hanako@example.com |
RECIPIENT | taro-foo@example.co.jp |
USER | taro |
HOST | example.co.jp |
LOCAL | taro-foo |
EXT | foo |
RECIPIENT変数の値は実際に配送される宛先のアドレスに なるのであって,メッセージに含まれる To: アドレスの値になるとは 限らない。たとえば,
To: hogehoge@example.co.jp Cc: taro-foo@example.co.jp
のように出されたメッセージでも,taro-foo に届くときは RECIPIENT=taro-foo@example.co.jp になる。
上記の変数をうまく利用すればメイル処理プログラムが効率的に作れる。 簡単な例として,送信者に「ありがとう」とだけ返すプログラムを作る。
メイルアドレスは user-39
で作る。
この配送先は ~/.qmail-39
ファイルに書き込んで作成する。
echo '| ./pf3/thankyou.rb' > ~/.qmail-39
ホームディレクトリから見て ./pf3/thankyou.rb
の名前で送信者($SENDER
)にメイルを送るRubyプログラムを
作る。その前にメイルを送信するコマンドについて調べる。
メイルを送信するための原始的なプログラム sendmail を利用するとプログラムでメイル送信が容易に行なえる。
sendmail -f from@add.ress to@reci.pie.nt...
これで標準入力を読んだ結果を送信する。ヘッダと本文を正しく入れて 送信してみる。
sendmail -f 自分のアドレス 自分のアドレス
To: 自分のアドレス
From: 自分のアドレス
Subject: test
Hello!
[C-d]
メイルヘッダのフィールド値に日本語を入れたい場合はMIMEエンコード する必要がある。また,本文もJISコード(ISO-2022-JP)に変換しておく 必要がある。いずれも nkf ライブラリで変換できる。元の文字列をMIMEエンコードするには
NKF.nkf('-jM', String)
とし,JISコードに変換するには
NKF.nkf('-j', String)
とする。日本語Subject付の日本語メッセージを送る簡単な プログラムは以下のようになる。
#!/usr/bin/env ruby # coding: euc-jp require 'nkf' subj = '日本語サブジェクト' body = 'こんにちは, さようなら。' if ARGV[0] == nil then STDERR.puts "送り先アドレスを指定して下さい。" STDERR.puts " 例: #{$0} toaddress@example.jp" exit 1 end command = sprintf("| sendmail %s", ARGV[0]) header = sprintf("To: %s Subject: %s Content-type: text/plain; charset=iso-2022-jp Mime-Version: 1.0\n\n", ARGV[0], NKF.nkf('-jM', subj)) open(command, "w") do |mail| mail.print header mail.print NKF.nkf('-j', body) end
~/.qmail-39
に指定したプログラムで,
送信者に「ありがとう」とだけ返すプログラムを作成してみる。
送信者はスクリプト起動時の環境変数 SENDER
から
取得し,そこにメイルを返すようにするのが簡単である。
プログラムは以下の流れで作成する。
~/.qmail-39
への送信により
qmailデーモンから起動される。このとき標準入力を読むと
送信メッセージの内容を1行目から順に得ることができる。
送信者のアドレスが環境変数 SENDER
に入っているので,これをスクリプトから送るメイルの
宛先とする。
スクリプトが走るきっかけとなった受信アドレスが
環境変数 RECIPIENT
に入っているので,
これをスクリプトから送るメイルの送信者アドレスとする。
SubjectはMIMEエンコードし,本文はJISコード変換してから送る。
環境変数は,Rubyの変数 ENV
にハッシュとして
入っている。たとえば,ENV["FOO"]
は,環境変数
FOO
が定義されている場合はその値が,定義されていない場合は
nil
が返る。
#!/usr/bin/env ruby
# coding: euc-jp
require 'nkf'
sender = ENV['SENDER'] # 環境変数SENDERの値の取得
rcpt = ENV['RECIPIENT'] # 環境変数RECIPIENTの値の取得
if sender == nil || rcpt == nil then
STDERR.puts "$SENDER and $RECIPIENT not set. exit."
exit 0 # メイル用プログラムはエラーでも exit 0 すべき
elsif /.*@.*/ !~ sender then # メイルアドレス形式でない場合
STDERR.puts "SENDER address invalid"
exit 0
end
to = sender
from = rcpt
subject = NKF.nkf("-jM", 'メイル受信しました')
header = sprintf("To: %s
From: %s
Subject: %s
Content-type: text/plain; charset=iso-2022-jp
Mime-Version: 1.0\n\n", to, from, subject)
message = NKF.nkf("-j", "ありがとう\n")
program = sprintf("| sendmail -f %s %s", from, to)
open(program, "w") do |mail|
mail.print header
mail.print message
end
メイル配送により起動するプログラムはエラーで停止してはならない。 デバッグする際はメイル配送時と同じ状況を作り出してプログラムを起動する。 このスクリプトの例では,
SENDER
と RECIPIENT
に値をセットするの2点を満たしつつプログラムを起動する。適当なメイル格納ファイルが
~/Mail/inbox/1
にあったとすると,
cat ~/Mail/inbox/1 | \
SENDER=自分のメイルアドレス RECIPIENT=スクリプトの受信アドレス \
./pf3/thankyou.rb
のようにして確認する。
起動実験で問題がなければ,実際に電子メイル経由で 起動してみる。
echo test|Mail -s test-mail スクリプトの受信アドレス