プログラムによるメイル受信

電子メイルはそのメッセージがファイルとして保存されるだけではなく, メッセージの内容を標準入力としたプログラムの起動も行なえる。

電子メイル配送のしくみ

電子メイルは,送信者がクライアントソフトウェアを用いてメッセージを送ると 送信者側のメイルサーバを経て受信者側のメイルサーバに届く(下図参照)。

Image of message processing via SMTP

中継点となるメイルサーバが複数あったり,ない場合もあるが, 最終的には受信者のメイルボックスのあるメイルサーバで, ファイル単位の書き込み処理がされる。

ここでは,MDA(Mail Delivery Agent)が,送られたメッセージをファイルに 書き込む時に自動処理を行なわせる方法を考える。

qmailのプログラム配送

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個目のハイフン以降
DEFAULTdefault でマッチした文字列

たとえば,hanako@example.com が taro-foo@example.co.jp 宛てに出したメイルが taro 宛に届く場合を考える。 ~taro/.qmail-foo

| program args...

のように書いてある場合の program が起動するとき, 環境変数は以下のように設定される。

環境変数
SENDERhanako@example.com
RECIPIENTtaro-foo@example.co.jp
USERtaro
HOSTexample.co.jp
LOCALtaro-foo
EXTfoo

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付の日本語メッセージを送る簡単な プログラムは以下のようになる。

sendjpex.rb

#!/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 から 取得し,そこにメイルを返すようにするのが簡単である。

プログラムは以下の流れで作成する。

環境変数は,Rubyの変数 ENV にハッシュとして 入っている。たとえば,ENV["FOO"] は,環境変数 FOO が定義されている場合はその値が,定義されていない場合は nil が返る。

thankyou.rb

#!/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

メイル配送により起動するプログラムはエラーで停止してはならない。 デバッグする際はメイル配送時と同じ状況を作り出してプログラムを起動する。 このスクリプトの例では,

の2点を満たしつつプログラムを起動する。適当なメイル格納ファイルが ~/Mail/inbox/1 にあったとすると,

cat ~/Mail/inbox/1 | \
  SENDER=自分のメイルアドレス RECIPIENT=スクリプトの受信アドレス \
  ./pf3/thankyou.rb

のようにして確認する。

起動実験で問題がなければ,実際に電子メイル経由で 起動してみる。

echo test|Mail -s test-mail スクリプトの受信アドレス

本日の目次

yuuji@e.koeki-u.ac.jp