2008年11月アーカイブ

前回の応用編として、バウンスされたエラーアドレスを、メルマガごとに処理してみる。
(過去記事:「メルマガのエラーアドレスやバウンスメールを自動的に処理する方法(PostFix)」)
(環境:fedora9、postfix2.23以降、perl5.8.X)

まず、VERPを利用の際に、sendmailパスを以下のようにしてみる。

sendmail -XV -f bounce+$magazine_name

もちろん$magazine_nameにはメルマガ名とかメルマガ番号とかが格納されてること。



次に、/etc/postfix/main.cf の設定は前回と同じ。




次に、バウンスメール処理用のプログラム(perl)を以下のようにカスタマイズする。
(設置場所は前回同様、/home/owner/に設置)

例:process_bounce.cgi(705 or 755)


#!/usr/bin/perl -w

my ($magazine_name, $deadadd);

#バウンスメールのヘッダ及び本文を標準入力から取得
while(<>){
    #最初に出てきたTo行から宛先を取得
    if ($_ =~ /^To: "?bounce\+(.+?)\+(.+?)\=(.+?)"?\@mydomain.jp\n$/){
        $magazine_name = $1;            #最初に設定した$magazine_nameが取得できる
        $deadadd = $2."@".$3;            #ここでエラーアドレスを取得
        last;
    }
}


#
#
#このあたりで、mysqlに接続するなどして、不逹アドレスを削除する処理
#
#取得した$magazine_nameの値を利用して、読者削除したい対象のメルマガが指定可能
#

exit;


※こうすることによって、データベース処理が楽になる。
※もう一つ前回と違う点は、ペーストする正規表現を、

  /^To: "?bounce\+(.+?)\+(.+?)\=(.+?)"?\@mydomain.jp\n$/

としたところ。

これは、相手先MTAが、バウンズを返すエンベローブアドレスを""でくくってよこす場合があるので、そのための対処。





postfixなどで、送信時にホストに接続できなかったりして再配送処理(deferやdeferred)に廻ってしまったメールキューは、手動で削除しない限り、デフォルトの5日間は数分おきに延々と再配送処理を繰り返す。
main.cfの設定でこの期間を短縮することは可能だが、ウザいので手っ取り早く消してしまいたいときは、管理者権限からエディタ(gedit)を起動して、

/var/spool/postfix/defer/メールIDの最初の1文字/メールID

のファイルを抹消すれば良い。

コマンドラインから行うときは、

# postsuper -d ALL defer (or deferred)

で実行可能。
ディレクトリを指定しない場合は、/var/spool/postfix/ 以下のすべてのキューが削除される。

command not found となってしまうときは、管理者権限を得るときに、
su じゃなくて、 su - で上位ディレクトリから行うとできるはずです。


なぜこの作業が必要かというと、前回の記事でバウンスメールの処理を行うにあたり、バウンスが返ってこなければ削除が行われないということに起因する。

deferが繰り替えされる要因としては、
1.ホスト名の解決はできたが、そのホストのDNSサーバに接続できずMXレコードが引けず、Connection timed out になるため、postfixは再送を試みる。
2.同じく、DNSサーバで名前解決はしたが、接続されるはずのメールサーバがダウンしている、もしくは存在しないため Connection timed out となり、postfixは再送を試みる。

これらが大半を占めると思われる。

なので、大量のメールアドレスを扱うメルマガ配信システムを持ってる場合等は、一日一回はメールキューの処理を行なったほうがいいかもしれない。
ただし、メールキューを削除すると、メールアドレスは正常であるにもかかわらず何らかの理由でdeferなどに一時的にまわったキューも削除されてしまうので注意。


「n回以上 Connection timed out になったキューは自動的に管理者にバウンスを返す」
みたいな設定ができればいいんだけどな・・・
もしかするとマニュアルをよく読めばできるのかな・・・

どなたかお知恵を貸してください。
おそらく、この世にメルマガ配信システムはたくさんあるので、この方法は
広く使われていると思うのだが、いくら検索しても具体的に説明されてるサイトが
ないので、忘れないようにメモ。
(環境:fedora9、postfix2.23以降、perl5.8.X)

まず、VERPを利用するので、sendmailを使う場合はパス指定時に、

sendmail -XV -f bounce

と指定。
※postfixのバージョンが2.2以前の場合は -XV じゃなく -V 。
※bounceの部分は別にhogeでも何でもよい。
※sendmailを使わない場合(SMTPに直接送るときとか)は、別にVERPを使わなくても、
エンベロープMailFromに
"バウンスを受けたい架空のアカウント"+"送信先のアカウント"="送信先ドメイン"@"自ドメイン"の形で指定できればOK。


次に、/etc/postfix/main.cf に以下を追記

recipient_delimiter = +
propagate_unmatched_extensions = canonical, virtual

※1行目は、送られてくるバウンスメールの宛先アドレスは、「+」で区切りますよ、とpostfixに教えている。
※2行目は、デリミタ以降(extensions)は、エイリアスに展開する際、無視しますよということをpostfixに教えている。
※これらの変数や定義値は、postfixのデフォルトなので、変更していればそれに合わせてください。


次に、バウンスメール処理用のプログラムをperlで作成。(ここでは/home/owner/に設置)

例:process_bounce.cgi(705 or 755)


#!/usr/bin/perl -w

my ($bouncetxt, $deadadd);

while(<>){    #バウンスメールのヘッダ及び本文を標準入力から取得
    #最初に出てきたTo行から不逹の宛先を取得
    if ($_ =~ /^To: bounce\+(.+?)\=(.+?)\@mydomain.jp\n$/){
        $deadadd = $1."@".$2;
        last;
    }
}

#
#
#このあたりで、mysqlに接続するなどして、不逹アドレスを削除する処理
#
#
#

exit;

※これは別にperlじゃなくても、CでもJavaでもshellでもなんでもいい。
※ポイントは標準入力からメール内容を受け取れるということ。



次に、/etc/aliases (OSやpostfixのヴァージョンによっては、 /etc/postfix/aliases とか)に以下を追記。

bounce:     |/home/owner/process_bounce.cgi

※エイリアスで、さっき書いたメール内容を処理するプログラムのコマンド(場所)をパイプで指定する。
※もし、セキュリティ上smrshなどの影響で、コマンドを渡せない場合は、コマンドラインから
# cd /etc/smrsh
# ln -s /home/owner/process_bounce.cgi process_bounce.cgi
などと打って対処する。



最後にエイリアスデータベースを初期化&postfix再起動

# newaliases
# /etc/init.d/postfix restart


これで、リターンメールやバウンスメールを受信する度にスクリプトが起動して、
死にアドレスを自動でデータベースから削除してくれるので、次回送信時から無駄な
送信負荷がなくなる。

ここでちょっと気になるのが、パイプでコマンドに渡されたメールキューは
どこにいくのかということだが、maillogを確認したところ、コマンドに渡した直後に
勝手にremoveされていたので一安心。
/var/spool/postfix をのぞいてみたらちゃんと削除されていた。


また、ホスト名が解決できない場合のバウンス(host not found)はすぐに返ってくるので、メルマガ送信後すぐに削除処理されるが、実在するホストの場合、アカウントが存在しなくてもホストのMTAによっては送信リトライを繰り返すような場合がある。
この場合、deferedフォルダにキューが溜まってしまうが、いずれアカウントが見つからない
といったバウンスが返ってくるので、24時間後とかに処理さる場合もあると思う。
(まだ未確認)

また、当たり前だが、相手先のMTAがアカウント不明の受信メールはすべて破棄し、バウンスを返さない設定になっていればこの方法は使えない。



2回目のMT設置が完了。
現在の最新バージョンはMT4.22。

なぜ2回目かというと、前回は2週間前に設置したが、設定ファイルを特に気にしてなかったので、mysqlに格納するときに、文字コードがおかしくなっていた。

これはmysql4になってから、クエリ発行時のデフォルトキャラクタセットが強制的にUTF-8になったことに起因する昔からの不具合らしく、MTだけを使っているのなら格納時に文字化けしても、取り出し時にエンコードされるので表面上は正常に見えるが、phpmyadminでDBを見てみると、見事に文字化けしている。
また、デフォルトでの文字セットと照合順序がlatin1_swedish_ci(スウェーデン?)になっちゃってる。

MTで使うmysqlサーバは、他の自分のスクリプトからも参照されるので、これはさすがに統一しなきゃならんってことで、いろいろ解決方法を検索して調べまくり今に至る。

結果的に/etc/my.confを以下のように変更し、文字化けは解消された。

[client]
default-character-set = utf8

[mysqld]
datadir=/var/lib/mysql
socket=/var/lib/mysql/mysql.sock
user=mysql
# Default to using old password format for compatibility with mysql 3.x
# clients (those using the mysqlclient10 compatibility package).
old_passwords=1
default-character-set = utf8
character-set-server = utf8
collation-server = utf8_general_ci
init-connect = SET NAMES utf8


[mysqld_safe]
log-error=/var/log/mysqld.log
pid-file=/var/run/mysqld/mysqld.pid
default-character-set = utf8

[mysqldump]
default-character-set = utf8

[mysql]
default-character-set = utf8

skip-character-set-client-handshake


最後のskip-character-set-client-handshakeオプションが有効になる環境であれば、
他のdefault-character-set = utf8とかは記述しなくても問題ないとは思われるが、念のためのおまじないってことで・・・


phpmyadminから再び確認すると、ちゃんと2バイト文字も文字化けなし。
いーねいーね。

上の設定をしておけばmt-config.cgiの内容もほぼデフォルトでOKみたい。


ただ、今まで書きためたMTの記事(10件ほど)は、すでに文字化けした状態でmysqlに格納されていたため、残念ながらリセット。
また一から記事の書き直しだ・・・

このアーカイブについて

このページには、2008年11月に書かれたブログ記事が新しい順に公開されています。

次のアーカイブは2008年12月です。

最近のコンテンツはインデックスページで見られます。過去に書かれたものはアーカイブのページで見られます。

ウェブページ

Powered by Movable Type 4.22-ja