このページは福井県立大学の田中求之が2006年1月まで運用していた Mac のサーバ運用に関する会議室 「Web Scripter's Meeting」の記録です。情報が古くなっている可能性がありますのでご注意ください。

CGIでのメールを送信について

発言者:RITZ
( Date Thursday, October 28, 2004 12:11:24 )


はじめまして。
ずいぶん検索して調べましたが、勉強不足のため解決策がみつからず、こちらに投稿させていただきます。アドバイスをお願い致します。OSはMac OS 9.2.2です。

グリーティングカードのスクリプトを外国のサイトからダウンロードしました。メールはテンプレートを読み込んで送られるようになっています。
カードを送ると相手にお知らせが届きますが、カードを送った人には確認メールが届かない(開封後に届く)ため、メール送信部分を以下のように書き換えてみて、メールは届くようになりました。
「$email1」のテンプレートを使ったメールは問題なく届きますが、「$email3」のテンプレートを使ったメールには「$email1」と「$email3」の文章が両方とも書き込まれてしまいます。

■card cgi:
$error = "./error.html";
$email1 = "./email1.txt";
$email3 = "./email3.txt";
$mailprog = '/usr/lib/sendmail';

$subject1 = "さん宛のカードが届いています";
$subject3 = "カードの送信が完了しました";

■program.cgi:
   open (MAIL, "|$mailprog -t") || die "Can't open $mailprog!\n";
   print MAIL "To: $VALUE{'MAILTO'}\n";
   print MAIL "From: $VALUE{'MAILFROM'} \n";
   print MAIL "Subject: $VALUE{'RECIPIENT'}$subject1\n\n";

   open(TEMPLATE,"$email1");
   &template;
   print MAIL $template;
   print MAIL ".\n" ;
   END;

   open (MAIL, "|$mailprog -t") || die "Can't open $mailprog!\n";
   print MAIL "To: $VALUE{'MAILFROM'}\n";
   print MAIL "From: $VALUE{'MAILTO'} \n";
   print MAIL "Subject: $subject3\n\n";

   open(TEMPLATE,"$email3");
   &template;
   print MAIL $template;

   print MAIL ".\n" ;
   close(MAIL);

   $deletes = "$cards$DATE.dat";

   if(-e $deletes){
   }
   else{
     open(DATA, ">>$deletes");
     close(DATA);
     &delete;
   }
   exit;
}

ちゃんと勉強もしてないのに無謀な挑戦だと反省しつつも、なんとかここまでできたので、完成させたいと思ってしまいます。
アドバイスをいただけたら幸いです。


→  ひなっこ グリーティングカード

cgi さんからのコメント
( Thursday, October 28, 2004 13:06:08 )

これだけでは分かりません。
email1.txtとemail3.txtの内容と、templateでどういう処理をしているかという事が必要です。

RITZ さんからのコメント
( Thursday, October 28, 2004 14:12:40 )

cgi さん、返信ありがとうございます。

>メールはテンプレートを読み込んで送られるようになっています。
このテンプレートというのはemail1.txtとemail3.txtのことです。
テンプレートとは呼ばないのかもしれません。すみません。

email1.txtとemail3.txtの内容は、「%%%SENDER%%%さん(%%%MAILFROM%%%)からカードが届いています」いったように
データを読み込むようになっている文章だけです。
「program.cgi」に以下のような記述があります。

#####################
#Calls the Template 
#####################

sub template {
     while (<TEMPLATE>){
       $template .= $_;
       }
     close(TEMPLATE);

        $template =~ s/\%%%PICTURES%%%/$VALUE{'PICTURES'}/g;
        $template =~ s/\%%%PICTURE%%%/$VALUE{'PICTURE'}/g;
        $template =~ s/\%%%SENDER%%%/$VALUE{'SENDER'}/g;
        $template =~ s/\%%%MAILFROM%%%/$VALUE{'MAILFROM'}/g;
        $template =~ s/\%%%MAILTO%%%/$VALUE{'MAILTO'}/g;
        $template =~ s/\%%%RECIPIENT%%%/$VALUE{'RECIPIENT'}/g;
        $template =~ s/\%%%MESSAGE%%%/$VALUE{'MESSAGE'}/g;
        $template =~ s/\%%%CGI%%%/$cgi/g;
        $template =~ s/\%%%MAX_DAYS%%%/$MAX_DAYS/g;
        $template =~ s/\%%%SHORTDATE%%%/$SHORTDATE/g;
        $template =~ s/\%%%url_of_card%%%/$url_of_card/g;
}

→   ひなっこ グリーティングカード

森下 さんからのコメント
( Friday, October 29, 2004 00:18:04 )

#Mac OS 9.2.2で、/usr/lib/sendmailはありえへんやろ、という突っ込みは置いておいて、

Perlにおいて、
こっちの場合にはこの処理を、
そうでなくてあっちの場合にはあの処理を

という構文は、
#!/usr/bin/perl
if(こっち){
 この処理;
}elsif(あっち){
 あの処理;
}

になります。
場合分けがうまくできてないんでしょう。
つまり、「なぜか、3のテンプレートを選ぶと、1と3の両方が勝手に出て行ってしまう。」
のではなく、「《あなたが》3のテンプレートを選んだ場合、1と3の両方を出すようにcgiに命じている。」
のです。

RITZ さんからのコメント
( Friday, October 29, 2004 16:40:02 )

森下さん、返信ありがとうございます。

>こっちの場合にはこの処理を、そうでなくてあっちの場合にはあの処理を

じつは、以下のように同時にどっちにも送信したいのです。
$email1 のアドレスに「email1.txt」を使ったメール
$email3 のアドレスに「email3.txt」を使ったメール

この場合は、どういった構文を使えば良いのでしょうか?

森下 さんからのコメント
( Friday, October 29, 2004 20:18:45 )

先に書いた構文を利用するなら、
foreach(メールアドレスのリスト){
 if (宛先がemail1の時){
  「email1.txt」を使ったメールを送る;
 }elsif (宛先がemail3の時){
  「email3.txt」を使ったメールを送る;
  }
}

ですね。
宛先が同時に100も200もあるなら、これではちょっと実用にならないでしょうが:-P

RITZ さんからのコメント
( Saturday, October 30, 2004 04:53:29 )

森下さん、ご親切にありがとうございます。
かなり頑張ってみたのですが、サーバーエラーになってしまいます。
知識不足が身に沁みます……、すみません。

open (MAIL, "|$mailprog -t") || die "Can't open $mailprog!\n";
foreach $email ('$VALUE{'MAILTO'}','$VALUE{'MAILFROM'}'){
  if ($email eq '$VALUE{'MAILTO'}'){
    print MAIL "To: $VALUE{'MAILTO'}\n";
    print MAIL "From: $VALUE{'MAILFROM'} \n";
    print MAIL "Subject: $VALUE{'RECIPIENT'}$subject1\n\n";

    open(TEMPLATE,"$email1");
    &template;
    print MAIL $template;
    print MAIL ".\n" ;
    close(MAIL);
  }
  elsif ($email eq '$VALUE{'MAILFROM'}'){
    print MAIL "To: $VALUE{'MAILFROM'}\n";
    print MAIL "From: $VALUE{'MAILTO'} \n";
    print MAIL "Subject: $subject3\n\n";

    open(TEMPLATE,"$email3");
    &template;
    print MAIL $template;
    print MAIL ".\n" ;
    close(MAIL);
  }
}

質問の際に書き間違っていましたが、メールの送信先は$VALUE{'MAILTO'},$VALUE{'MAILFROM'}でした。
foreach $emailの$emailという変数ですが、なにを基準に当てはめたら良いのやら、わかりませんでした。
もしかして、間違いだらけでしょうか?


森下 さんからのコメント
( Saturday, October 30, 2004 10:49:28 )

foreachの後は、変数ではなく配列が入ります。
つまり、今回の場合、送るべき宛先全てのリストになりますね。
宛先をすべて入れた配列を@emailとした場合、
foreach(@email){この処理;}
という構文で、@emailに入っているメールアドレス一つ一つに対して、
一つずつ順番に「この処理」が行われます。
つまり、foreach()は繰り返しを指示する関数です。

ところで、変数というのは何かの入れ物ということはわかっていますか?
例えば、$emailというのは、それだけでは何かの入れ物というだけであって、
あなたがここにメールアドレスを入れない限り、
perlが勝手にメールアドレスとして処理をしてくれるというものではありません。
逆に言えば、$emailという名前の入れ物に、性別のデータを入れることも可能です。

所詮プログラムなんて、どの言語であれ、何か入れ物を作って、
その時々の何かを入れ物に当てはめて、
その入れ物に対して足したり引いたりなにか処理をするというだけで、
あとはこの場合にはと言う場合分けを入れて、作業の順番を指示するだけです。

ということは、他人が作ったプログラムを何か改造しようと思ったら、
まずそのプログラムがどういう入れ物を用意していて、
そこには何を入れるように指示していて、
どのように場合分けされていて、
それがどのような順番に組み合わさっているのかを把握しないと、
手をつけられないことになります。

最初の段階でそれができていないのなら、まず改造は不可能だと思いますよ。
よしんばうまく動作したとしても、重大なセキュリティーホールを抱え込んだりする
可能性も大きくなります。
まずはいじくる前に、改造前の全体像をフローチャートにしてみることをお勧めします。
そうすれば、どこをどういじくれば良いのか、見えてくるでしょう。

それと、perlの関数のリファレンス本くらいは手元に置く方が良いでしょう。
そうすれば、foreach()の()の中は配列ということぐらい、すぐにわかるでしょうしね。;-P


森下 さんからのコメント
( Saturday, October 30, 2004 11:02:45 )

あっと、ちょっと勘違いしてましたが、
一動作当たり送られる宛先は常に2箇所なんですか?
だとするならわざわざforeach()で回す必要は無いです。
foreach()は普通、いくつあるかそのときにならないとわからないものに対して使います。

後は、フローチャートのアドバイスはそのまま。

それと、一点気になったのですが、
文字列を示すのに、
"  "でくくるのと、 '  'でくくるのとでは、動作が変わるのはご存知ですか。

例えば、$email eq '$VALUE{'MAILTO'}'と書いてられますが、
これは意味不明ですよ。


RITZ さんからのコメント
( Saturday, October 30, 2004 16:46:54 )

森下さん、丁寧なお返事をありがとうございます。
やっとhtmlが理解できたという程度ですのに、必要に迫られてperlの即席の勉強をはじめてみたところですが、
即席なんぞで動かせるものではないとしっかりわかりました。
田舎暮らしで近くにろくな本屋さんもないので、とりあえずネットで初心者用 perlの基礎講座から読んでいます。
枯渇しつつある頭の体操と思って、少しづつでもちゃんと勉強してみようと思います。
これからもよろしくお願い致します。

>一動作当たり送られる宛先は常に2箇所なんですか?
二ケ所だけです。
二ケ所だけなので、最初のスレッドのように書き足してみたのですが、
TEMPLATE,"$email1" が閉じていないようです。
もうちょっと頑張ってみます。

cgi さんからのコメント
( Saturday, October 30, 2004 17:49:39 )

頭の体操は大変結構な事だと思います。それにperlを選ばれた事も喜ばしき事と思います。
田舎暮らしと言ってもInternet接続は出来ている様なので、その気になれば、
何の問題もない様に思います。本は本屋に行かなくてもネットで購入出来るし、
情報は本だけではなくネットで十分に得られます。
どうかがんばってください。

RITZ さんからのコメント
( Saturday, October 30, 2004 23:08:47 )

cgi さん、ありがとうございます! 忘れてしまわないうちに頑張ります!
とてもややこしいものと思っていましたが、森下さんのコメントを読ませていただいた後で、
理解しようと思いながら初心者用講座などを読んでみたところ、逆に興味が湧いてきました。
早速、ネットで本も調べてみましたが、こちらは多過ぎて決めかねてます(^^;
お薦めの本(初心者向け)などあれば、教えていただけると嬉しいです。

上記のメールの件は、とりあえず以下のようにしてみました。
$VALUE{'MAILTO'}へ送るメールはこれまで通りTEMPLATE,"$email1"に書き込む
$VALUE{'MAILFROM'}へ送るメールはprogram.cgi に書き込む形にしてみました。

   open (MAIL, "|$mailprog -t") || die "Can't open $mailprog!\n";
   print MAIL "To: $VALUE{'MAILTO'}\n";
   print MAIL "From: $VALUE{'MAILFROM'} \n";
   print MAIL "Subject: $VALUE{'RECIPIENT'}$subject1\n\n";

   open(TEMPLATE,"$email1");
   &template;
   print MAIL $template;
   print MAIL ".\n" ;
   close(MAIL);

   open (MAIL, "|$mailprog -t") || die "Can't open $mailprog!\n";
   print MAIL "To: $VALUE{'MAILFROM'}\n";
   print MAIL "From: $VALUE{'MAILTO'} \n";
   print MAIL "Subject: $subject3\n\n";
   print MAIL "$sybject\n";
   print MAIL "\n\n";
   close(MAIL);

動くことは動いていますし、メールもちゃんと届いていますが・・・不安です。。
なにか問題があるようでしたら、教えていただけると幸いです。

otsune さんからのコメント
( Wednesday, November 03, 2004 04:01:00 )

CPANのMail-Senderというモジュールを使えばかなり簡単にできるのではないでしょうか。

→  CPAN Mail-Sender