perlメールフォームで文字コードUTF-8のテンプレートを使う

perlのソースコードはEUCで書くこととされていますが、現在はなにかとUTF-8を使いたいものです。perl5.8以降はunicode対応もされているようですが、perl5.6.1で対応したメモです。

概要

・メールフォームを作る
・HTML::Templateテンプレートライブラリを使う
・複数のページに共通したテーマを使う
・メール本文に投稿内容を代入するのにテンプレートを使う
・jcodeおよびJcode.pmは使わない
・Unicode::Japaneseを使う
・投稿内容のパースにcgi-lib.plを使う

 

ライブラリを読み込みます

(サーバーにライブラリがインストールされていれば不要です)
CPAN等からライブラリを入手し、「lib」フォルダなどを作って配置します。

use lib '(libへのフルパス)';
use HTML::Template;
use Unicode::Japanese;
require "cgi-lib.pl";

 

HTML::Templateテンプレートライブラリの使い方

ソースコードにhtmlの内容を埋め込まずに外部ファイルとして用意します。
これでデザイナーとの協業やメンテナンスが容易になります。
HTML::Temlateを使うと変数の埋め込みやIF条件などを使うことができます。

1.読み込むテンプレートファイルを作成します。
テーマテンプレート”theme.html”

<html xmlns="http://www.w3.org/1999/xhtml" lang="ja" xml:lang="ja">
<head>
<meta http-equiv="Content-Type" content='text/html; charset="UTF-8"' />
<title>template</title>
</head>
<body>
<h1>共通テーマ</h1>
<TMPL_VAR NAME=CONTENTS>
</body>
</html>

本文テンプレート”tpl.html”

<h2>本文</h2>
<TMPL_VAR NAME=ZIP>
<TMPL_VAR NAME=ADDRESS>
<TMPL_VAR NAME=TEL>

2.テンプレートを読み込み、いったん出力を変数に保持します。

$tplfile = "tpl.html";
# テンプレート読み込み
$template = HTML::Template->new(filename => $tplfile,'die_on_bad_params' => 0);
# テンプレートに変数を代入する
$template->param(ZIP => "373-****");
$template->param(ADDRESS => "群馬県太田市******");
$template->param(TEL => "0276-**-****");
# テンプレート出力
$data = $template->output;

2.保持した変数をテーマテンプレートに入れて出力します。

$themetplfile = "themel.html";
# テーマテンプレート読み込み
$template = HTML::Template->new(filename => $themetplfile,'die_on_bad_params' => 0);
# テーマテンプレートに変数を代入する
$template->param(CONTENTS => $data);
# テーマテンプレート出力
$data = $template->output;

# 出力する
print "Content-Type: text/html\n\n", $themetemplate->output;

 

cgi-lib.plの使い方

my %in;
&ReadParse(\%in);

これだけで受信したフォームデータが$in{'(name属性値)'}などで使うことができます。
たとえば、
<input type="text" name="hoge">

$in{'hoge'}
で使えます。

テンプレートファイルをutf-8で作成すれば、受信データもutf-8になります。

 

メール送信

本来、日本語でメール送信するには文字コードiso-2022-jp形式で行います。
gmailなどのウェブメーラーはutf-8とかでメールを送り、受信するメーラと中継するメールサーバーが対応していればかまわないようですが。
本文以外のメールヘッダーの件名(Subject)、From,Toに日本語を使うときはさらにbase64エンコードをする必要があります。

1.メール本文のテンプレートファイルを作成します。
これもUTF-8で作成します。
メール本文テンプレート”mail.txt”

お問合せがありました
<TMPL_VAR NAME=hoge>

2.件名をjisコードにして、さらにbase64エンコードします

$mlsb = "メールフォームからお問合せ";
$enc_mlsb = Unicode::Japanese->new($mlsb,'auto');
$enc_mlsb = $enc_mlsb->conv('jis');
$enc_subject= &Base64Encode($enc_mlsb);

sub Base64Encode {
 my ($target) = @_;
 my ($base) = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
   ."abcdefghijklmnopqrstuvwxyz"
   ."0123456789+/";
 my $eStr = "";
 my $pStr = unpack("B*",$target);
 for(my $i = 0;my $cStr = substr($pStr,$i,6); $i += 6){
  $eStr .=substr($base,ord(pack("B*","00".$cStr)),1);
  if(length($cStr) == 2){ $eStr .= "==";}
  elsif(length($cStr) == 4) { $eStr .= "=";}
 }
 return("=?iso-2022-jp?B?$eStr?=");
}

Unicode::Japaneseでは、
$enc_mlsb->conv('jis', 'base64')
として直接base64エンコードできますが、なぜか末尾に改行がついてメールヘッダーとして使えませんでした。
この改行を取れば、「"=?iso-2022-jp?B?」と「?="」で囲めばSubjectヘッダーに使えます。

3.メール本文をテンプレートで生成する

$mailfile = "mail.txt";
$mailtemplate = HTML::Template->new(filename => $mailfile,'die_on_bad_params' => 0);
$mailtemplate->param(hoge => $in{'hoge'});
$body = $mailtemplate->output;

4.メール送信する

$mailer = '/usr/sbin/sendmail';
$mlfr = "(送信先メールアドレス)";
$err = 0;
open(MAIL, "| $mailer -t -f'$mlfr'") or $err = 1;
if ($err == 0) {
 print MAIL "From: $mlfr\n";
 print MAIL "To: $mlfr\n";
 print MAIL "Subject: $enc_subject\n";
 print MAIL "MIME-Virsion: 1.0\n";
 print MAIL "Content-Type: text/plain; charset=iso-2022-jp\n";
 print MAIL "Content-Transfer-Encoding: 7bit\n\n";
 print MAIL Unicode::Japanese->new($body,'auto')->jis;
  close(MAIL);
} else {
  $errstr .= 'お客様メール送信エラー';
}

Unicode::Japaneseはjisエンコード指定で、iso-2022-jp形式にしてくれます。
また、いろいろテストしていた最中に
$enc1 = Unicode::Japanese->new($mlsb,'auto')->jis;
$enc2 = Unicode::Japanese->new($mlsb,'auto')->euc;
$enc3 = Unicode::Japanese->new($mlsb,'auto')->get;
などとしているとおかしな結果が得られました。
$enc = Unicode::Japanese->new($mlsb,'auto');
$enc1 = $enc->jis;
$enc2 = $enc->euc;
$enc3 = $enc->get;
としていったん文字列オブジェクトとしてから使うほうがよいようです。