CGI Mail

CGIからメールを送信する手法は非常に多くのサイトや図書で公開されていますが、 これほど、不十分な情報の多い項目も珍しいでしょう :-p

それは、「こんなこともできる」という Perl言語の紹介であったり、 「日本語?そんなもんは知らん!オレはアメリカ人だ」まで、 いろいろな理由が有りそうですが...

私も書物やWebページを参考にして、結構ハマりました

一般に紹介されているものは次のようなものでしょう

#!/usr/bin/perl

# sendmail のパスはサーバにより異なる
$sendmail = '/usr/lib/sendmail';

$to = 'You@YourDomain';

if(open(MAIL,"| $sendmail $to")){
    print MAIL <<END_OF_MAIL;
From: I@MyDomain
Subject: Test mail

This message is test
END_OF_MAIL
    close(MAIL);
}

これでも、一概に間違いとは言いきれませんが、 現実的には若干の問題を含んでいますし、 これを応用しようとしても実際にはうまく行かないことが多いです
理由としては以下の項目が挙げられます

ピリオドのみの行の対処

本文にピリオド「.」のみの行( エンド行 )があると、 sendmail は送信メッセージの最後と判断します

送信する本文にエンド行が無い保証が無い限り、エンド行を抑止する必要があります ( この対応を記述してある Webページや参考書は意外に少ない )

対処方法

エンド行を他の文字に変換する

$mailbody =~ s/(^|\n)\.(\n|$)/$1. $2/g;
$mailbody =~ s/(^|\n)\.(\n|$)/$1. $2/g;

「とほほの WWW入門」 の wwwmail.cgi より

sendmail のオプションで指定する

open(MAIL,"| $sendmail -oi 他のオプション")

sendmail のマニュアルより

日本語を含む文書

日本語を本文に使用した場合は文字コードが JISコードでなければ 文字化けを起こすメーラもあります
( 最近は文字コードの自動判別をするメーラが増えているので 読める場合もあります )

日本語を含む本文をメールで送信する場合は JISコードに変換する必要があります
( この対応を記述してある Webページは比較的多いが、 翻訳ものの参考書等には記述されていない )

対処方法

require 'jcode.pl';
     :
jcode::convert(\$mailbody,'jis');

または

use Jcode;
     :
Jcode::convert(\$mailbody,'jis');

日本語を含むヘッダに対する対応

Subject には基本的には英数文字を使用する約束になっていますが、 最近は日本語を含む Subject を使用するケースが増えてきています

そのため、本文は読めるが Subject が文字化けを起こすケースがあります

Subject 等メールヘッダに日本語などの文字を含む場合は MIME encode するか quoted printable に変換する必要があります
( この対応を記述してある Webページや参考図書は少ない )

require 'mimew.pl';
    :
$mailheader = mimeencode <<END_OF_HEADER;
From: I\@MyDomain
To: You\@YourDomain
Subject: テストメール

END_OF_HEADER

または

use Jcode;

my $str = <<END_OF_HEADER;
From: I\@MyDomain
To: You\@YourDomain
Subject: テストメール

END_OF_HEADER
$mailheader = Jcode->new(\$str)->mime_encode;

mimew.pl は生田氏作成の MIME encoder library

セキュリティホールに対する対策

上の例では sendmail の引数に宛先を指定して起動していますが、 サーバに telnet 等でログインして起動しているプロセスを見れば 送信先メールアドレスを見ることができます

また、送信先を HTML FORM から入力させるケースでは、 送信先に悪意を持ったコマンドを記述することで、 サーバアタックの踏み台にできます

送信先アドレスを隠蔽するために「-t」オプションを指定して、 メールヘッダから送信先を指定すべきです
( セキュリティを指摘しているページや参考図書はあまり多くないが、 直接宛先を指定して起動するスクリプトも多くはない )

対処方法

open(MAIL,"| $sendmail -t 他のオプション")

sendmail のマニュアルより

メール送信をする場合、現実的には上の例のようにハードコーディングではなく、 HTMLで記述された FORM や何らかのデータベースを元にメッセージを構築するので、 他に考慮すべき点もあります

メールアドレスのチェック

メールアドレスを HTML の FORM から指定させる場合には 特に入力ミスに対するチェックが必要でしょう

メールアドレスの仕様は RFC2822( Internet Message Format ) にありますが、 かなり複雑です

対処方法

この仕様に従ったチェックを行う簡単な方法は CPAM モジュールである Email::Valid を使用することでしょう

use Email::Valid;
    :
unless(Email::Valid->address($mailaddr)){
    # NG
}

このモジュールは、必要なら MX チェックもできるようです( ただし、遅い )

use Email::Valid;
    :
eval {
    unless(Email::Valid->address(
        -address => $mailaddr,
        -mxcheck => 1));
};
unless($@){
    # NG
}
送信エラーに対する対処

メールアドレスを HTML の FORM から指定させる場合には 上記項目の他に送信エラーに対する対処が必要でしょう

CGI メールからメールを送信した場合、該当のメールアドレスが存在しなかったり 不達の場合には、送信元にエラーメールとして戻る場合があります

然し、エラーメールの発信者は CGI そのものですから、 そのような事態が発生しても認識できません

対処方法

何にでも通用する解決方法はないようです
然し、条件付きでいくつか対策ができます

Errors-Toヘッダを指定する

$mailheader = <<END_OF_HEADER;
From: I@MyDomain
To: You@YourDomain
Errors-To: Owner@MyDomain
Subject: Test Mail

END_OF_HEADER

Errors-To:ヘッダを付加しても、 別途送信者にもエラーメールが送信されるようです
また、Errors-To ヘッダ自体廃止方向のようで、 sendmail以外では通用しなかったり、 このヘッダを無視する設定( sendmail R8 以降 )にしていたりするようです

送信者情報( Sender envelope address )を指定する

sender='Owner@MyDomain';
open(MAIL,"| $sendmail -f $sender 他のオプション")

* 基本的に送信者情報の変更を指定できるのは trusted user として登録されている 必要があり、 sendmail R5 では登録されていないと指定できませんし、 sendmail 8.7 以降では X-Authentication-Warning: ヘッダが付加されるようです
( このヘッダの使い方に関しては、情報があまりないのでわかりませんが、 ひょっとするとフィルタするメーラがいるかもしれません )

sendmail のマニュアルより

* また、この方法で指定する送信者アドレスは Form 等で入力するものではなく、 固定のアドレスにした方が良いことは言うまでもありません

英数記号を除く半角文字の駆除

送信するメールの本文の一部又は全部を HTML の FORM から指定する場合、 JIS コードで表現できない半角カナや特殊記号が含まれていると 受信側では文字化けが発生することになります

対処方法

補助漢字や特殊記号は対応することが難しいですが、 半角カナを全角文字に変換することは可能です

require 'jcode.pl';
    :
jcode::convert(\$mailbody,'jis','euc','z');

または

use Jcode;
    :
Jcode::convert(\$mailbody,'jis','euc','z');

convert 関数の第3引数は入力側の文字コードで、 undef や '' でも全角に変換されるようですが、 運が悪いと文字化けがおこります
できる限り入力側の文字コードを指定するようにして下さい

セキュリティホールの可能性

メーラの中には Netscape Mail のように HTML や JavaScript を 解釈するメーラがあります

HTML の FORM からメールの本文の一部又は全部を作成する場合、 たとえ Content-Type ヘッダが text/plain であったとしても HTML文を解釈して実行する可能性があります

そのため、 HTML のタグを無効にすべきかもしれません
( またタグの無効化を安易に行なうと、 その意図がない文章も無効化されてしまうので、 無効化の手法は検討の余地があります )

対処方法

万人が幸せになれる対処方法を思いつけません。f(^^;

一応、上記項目を対応すれば文字化けしないメールが出せるのですが、 このままではメールヘッダに Content-Type, Content-Transfer-Encoding など 送信データに関する情報が含まれていませんので内容に応じて 付加しないと受信者側で旨く表示できない場合があります

Content-Type: text/plain; charset=ISO-2022-JP
Content-Transfer-Encoding: 7bit
Mime-Version: 1.0