最終更新日 2005/3/26  戻る  472605 人 REMOTE = 38.103.63.18 REFERER =

★簡単なメール送信CGI(添付ファイル付き)

Words : CGI boundary 添付ファイル アップロード メール
メール送信のCGIを理解するためにはまずboundaryを理解しなければいけない.
アップロードや添付ファイル付きのメールも同じである.
boundaryってなに? boundaryとはそのまま境界という意味だ.英語では国境だとかそんなところで使う.で,最近perlを勉強している俺としてはどうしてこんな事を言うのかというと,ま〜いろいろあるのである.まずは,添付ファイルの付いたメールのヘッダを見たことがあるだろうか? この中は「件名−本文−添付ファイル」となっているはずだが,この本文と添付ファイルのデータの区切りがboundaryなのである.以下は俺が書いたメール発信のperlのサブルーチンなのだが(Content-Typeのあとは1行開けて下さい)データが$boundで区切られていることが分かると思う.最後は--$bound--で終わるのである.

##$Sendmailを使ってメールを送信するサブルーチン
sub mail_send {
## $youad--From,$mail--To,$subject--Subject,$honbun--本文,
## $fname--添付ファイル名,$ctype--ファイルのContent-type,$f_dat--添付ファイルのデータ
## $t_dat--$file_datをBase64でエンコードしたデータ
  local($yourad,$mail,$subject,$honbun,$fname,$ctype,$f_dat) = @_;
  local($bound,$mail_dat,$t_dat);
  #このboundaryによってデータが分けられる
  $bound='--wq5se3d1ew';
  #subjectヘッダをMIMEエンコードする
  $subject = &mimeencode($subject);
  &jcode::convert(¥$honbun,'jis');
  if ($fname eq '') {
    $mail_dat=<<"EOL";
X-Mailer: yaguma.com by Isamu Tanaka
X-HTTP_REFERER: http://yaguma.com
To: $mail
From: $yourad
Subject: $subject
Content-Type: text/plain; charset="iso-2022-jp"
Content-transfer-Encoding: 7bit

$honbun
EOL
  } else {
  #ファイルデータをBase64でエンコードする
  $t_dat = &bodyencode($f_dat);
  $t_dat .= &benflush;
  $mail_dat=<<"EOL";
X-Mailer: yaguma.com by Isamu Tanaka
X-HTTP_REFERER: http://yaguma.com
To: $mail
From: $yourad
Subject: $subject
MIME-Version: 1.0
Content-Type: multipart/mixed; boundary=$bound

--$bound
Content-Type: text/plain; charset=ISO-2022-JP
Content-Transfer-Encoding: 7bit

$honbun
--$bound
Content-Type: $ctype; name=$fname
Content-Transfer-Encoding: base64
Content-Disposition: attachment; filename=$fname

$t_dat
--$bound--
EOL
  }
## sendmailによる送信
      open(MAIL,"|$Sendmail -t") || &error(150,"$mail は送信に失敗しました");
      print MAIL $mail_dat;
      close(MAIL);
}
一番に言いたいことは,このboundaryはメールの様式だけではなかったことだ.GetメソッドやFormメソッドの引数は普通,「?」の後にデータ名=データ&データ名=データ&...となるが,添付ファイルがある場合全く違う.htmlファイルにpostメソッドで
enctype="multipart/form-data"
を加えるとファイルの内容が得られる.この時の引数が以下のようである.
-----------------------------7d5174b100252 Content-Disposition: form-data; 
name="name" a 
-----------------------------7d5174b100252 Content-Disposition: form-data; 
name="yourad" b 
-----------------------------7d5174b100252 Content-Disposition: form-data; 
name="mail" c 
-----------------------------7d5174b100252 Content-Disposition: form-data; 
name="subject" d 
-----------------------------7d5174b100252 Content-Disposition: form-data; 
name="honbun" e 
-----------------------------7d5174b100252 Content-Disposition: form-data; 
name="t_file"; filename="test1.cgi" Content-Type: text/html 
#! /usr/bin/perl print "Content-type: text/html¥n¥n"; print "¥n"; print "
Hello World ! 
こんにちわはじめてのぱーるです
¥n"; print"¥n"; exit; 
-----------------------------7d5174b100252-- 

引数が全てboundaryで区切られている.で,最後は--boundary--です.
次に簡単なメール送信CGIを書いてみた.

★使用したライブラリについて
jcode.pl-------言わずと知れた,日本語文字コードを変換するためのライブラリ.
mimew.pl------日本語メールを送るためにはSubjectヘッダをMIME エンコードしなくてはいけない.また,添付ファイルがある場合それをBase64でエンコードしなければならない.これをやってくれるのがこのライブラリ.
cgi-lib.pl-------ReadParseというサブルーチンを含んでおり,フォームの入力データを得ることができる.要するに,上記のboudaryに区切られたデータを効率よく取得することができる.

これらのライブラリはインターネット上で取得することができます.

★ヘッダについて
Content-Type: multipart/mixed; boundary=$bound
というのはメールの中がいろんなデータに分かれているということです.
例えば本文+添付ファイル+添付ファイル,本文(plaintext)+本文(html)のような感じで.これらの区切りがboundaryとなります.

Content-Type: $ctype; name=$fname
という行がありますが,これはファイルの種類とファイル名を表しています.
添付ファイルがgifファイルなら
Content-Type: image/gif; name=$fname
バイナリーファイルなら
Content-Type: application/octet-stream; name=$fname
テキストファイルなら
Content-Type: text/plain; name=$fname
のようになります.
Content-Disposition: attachment; filename=$fname
MIMEのbase64の場合,昔は上記のようにContent-Type:フィールドのname=パラメータとしてファイル名を示していましたが,これは元々間違いだそうです.現在はContent-Disposition:フィールドのfilename=パラメータで示す事になっています.しかし,どちらで扱うかはソフトによって様々なのでトラブルの元になります.ちなみにBeckyではContent-Type:フィールドのname=パラメータがあればContent-Disposition:がなくても使えますし,Beckyから送ったメールには両フィールドとも記入されています.
参考サイト

#!/usr/bin/perl
require 'jcode.pl';
require 'mimew.pl';
require 'cgi-lib.pl';
##########
#初期設定
##########
$Sendmail = '/usr/sbin/sendmail';        #sendmailのフルパス
$url = 'http://yourURI/';                #このスクリプトがあるURI
                                         #最後は/を付けて下さい
$max_f_size=600000;                      #添付ファイルのサイズ制限(バイト)
$buffer_size=1024;                       #バッファーサイズ
$self='mail_send.cgi';                   #このスクリプト名
$from_name='hoge';                       #名前
$from_mailadd='hoge@yaguma.com';         #送信元メールアドレス
$to_mailadd='hogehoge@hoge.com';         #送信先メールアドレス

#戻るべきURI
$url.=$self;

# フォームの入力データを得る
&ReadParse;
#最初paramという変数で書き始めたため本来の型グロブ値inをparamに代入した
*param=*in;
#パス付きのファイルネームとContent-typeを取得する
foreach $tmp (@param)
{
  if ($tmp =~ /(.*)Content-type:(.*)/i) { $ctype=$2; } 
  if ($tmp =~ /(.*)filename=(.*)/i) { $fname=$2; } 
}
#fnameからいらないデータを取り除く
$fname=~ s/"|¥n|¥r//g;

if($param{'sosin'}) {
  $mode="sosin";  #メール送信モード
} else {
  $mode="write";  #HP表示モード
}

if($mode eq "sosin") {

  #メールの件名
  $subject = $param{'subject'};
  
  # データが入力されていなければエラーサブルーチンを実行して終了
  if($param{'name'}){
    $name = $param{'name'};
    $from_name = $param{'name'};
  }else{
    &error(100,'お名前をご記入ください');
  }
  
  if($param{'mail'}){
    $mail = $param{'mail'};
    $to_mailadd = $param{'mail'};
  }else{
    &error(105,'送信先メールアドレスをご記入ください');
  }
  if($param{'yourad'}){
    $yourad = $param{'yourad'};
    $from_mailadd = $param{'yourad'};
  }else{
    &error(110,'送信元メールアドレスをご記入ください');
  }

  if($param{'mess'}){
    $mess = $param{'mess'};
    $mess =~ s/¥r¥n/¥n/g; #改行コードを¥nに変換
  }else{
    &error(120,'書き込みしてください');
  }
  
  if($param{'t_file'}){
    $f_dat = $param{'t_file'};
  }
  #else{
  #  &error(130,'添付ファイルを選んでしてください');
  #}
  #メールを送信する
  #ファイルサイズを調べ$max_f_size以上ならエラーにする
  $file_size=length($f_dat); 
  if($file_size > $max_f_size){ 
    &error(140,"ファイルサイズが大きすぎます.$max_f_size Bytes 以下にして下さい."); 
  }
  #メール送信のサブルーチン
  &mail_send($yourad,$mail,$subject,$mess,$fname,$ctype,$f_dat);
  #メールを送信したと表示する
  &put_form_sosin;

} else {

  #フォームを表示する
  &put_form;
}

exit;

##$Sendmailを使ってメールを送信するサブルーチン
sub mail_send {

## $youad--From,$mail--To,$subject--Subject,$honbun--本文,
## $fname--添付ファイル名,$ctype--ファイルのContent-type,$f_dat--添付ファイルのデータ
## $t_dat--$file_datをBase64でエンコードしたデータ

  local($yourad,$mail,$subject,$honbun,$fname,$ctype,$f_dat) = @_;
  local($bound,$mail_dat,$t_dat);
  #このboundaryによってデータが分けられる
  $bound='--wq5se3d1ew';
  #subjectヘッダをMIMEエンコードする
  $subject = &mimeencode($subject);
  &jcode::convert(¥$honbun,'jis');

  if ($fname eq '') {
    $mail_dat=<<"EOL";
X-Mailer: yaguma.com by Isamu Tanaka
X-HTTP_REFERER: http://yaguma.com
To: $mail
From: $yourad
Subject: $subject
Content-Type: text/plain; charset="iso-2022-jp"
Content-transfer-Encoding: 7bit

$honbun
EOL
  } else {
  #ファイルデータをBase64でエンコードする
  $t_dat = &bodyencode($f_dat);
  $t_dat .= &benflush;
  $mail_dat=<<"EOL";
X-Mailer: yaguma.com by Isamu Tanaka
X-HTTP_REFERER: http://yaguma.com
To: $mail
From: $yourad
Subject: $subject
MIME-Version: 1.0
Content-Type: multipart/mixed; boundary=$bound

--$bound
Content-Type: text/plain; charset=ISO-2022-JP
Content-Transfer-Encoding: 7bit

$honbun
--$bound
Content-Type: $ctype; name=$fname
Content-Transfer-Encoding: base64
Content-Disposition: attachment; filename=$fname

$t_dat
--$bound--
EOL
  }
## sendmailによる送信
      open(MAIL,"|$Sendmail -t") || &error(150,"$mail は送信に失敗しました");
      print MAIL $mail_dat;
      close(MAIL);
}

# エラー表示サブルーチン
sub error {
  my($num,$word)=@_;

  print <<"EOL";
Content-type: text/html; charset=Shift_JIS

<html>
<head>
  <title>エラー</title>
</head>
<body>

 以下のエラーがあります。<br><br>

 No. $num<br>
 $word
 <a href=$url>戻る</a>

</body>
</html>
EOL

  exit;

}
# メール送信後のサブルーチン
sub put_form_sosin {
  print <<"EOL";
Content-type: text/html; charset=Shift_JIS

<html>
<head>
  <title>メール送信</title>
</head>
<body>
名前   : $from_name<br>
送信元 : $from_mailadd<br>
送信先 : $to_mailadd<br>
メールが送信されました.<br><br>
<a href=$url>戻る</a>
</body>
</html>
EOL
  exit;
}
# フォーム出力のサブルーチン
sub put_form {                          #フォームを出力する
  print <<"END";                        #ENDまで出力
Content-Type: text/html; charset=Shift_JIS

<html>
<head>
  <title>メール送信</title>
</head>
<body>

<form action=$self enctype="multipart/form-data" method="post">
<table>
  <tr>
    <td>名前:</td>
    <td><input type="text" size="20" name="name" value=$from_name></td>
  </tr>
  <tr>
    <td>送信元メアド:</td>
    <td><input type="text" size="20" name="yourad" value=$from_mailadd></td>
  </tr>
  <tr>
    <td>送信先メアド:</td>
    <td><input type="text" size="20" name="mail" value=$to_mailadd></td>
  </tr>
  <tr>
    <td>件名:</td>
    <td><input type="text" size="20" name="subject" ></td>
  </tr>

  <tr>
    <td>本文を書き込み</td>
    <td><textarea name="mess" cols="40" rows="5" ></textarea></td>
  </tr>
  <tr>
    <td>添付ファイル</td>
    <td><input type="file" name="t_file"></td>
  </tr>
  <tr>
    <td colspan="2" align="center">
      <input type="submit" value="送信" name="sosin">
    </td>
  </tr>
</table>

</body>
</html>
END
  exit;
}

# デバッグ用 変数を表示する
sub debug_exit {
  local($a,$b,$c,$d,$e,$f,$g) = @_;

print <<"END";                                  #ENDまでのデータを出力
Content-Type: text/html; charset=Shift_JIS

<html>
<br>[1]=$a <br> [2]=$b <br> [3]=$c <br> [4]=$d <br> [5]=$e <br> [6]=$f <br> [7]=$g<br>

</body>
</html>
END
  exit;
}


Copyright 2002-2003 Kai All Rights Reserved & This site is Link Free 戻る