~出典:2000年5月1日 日経バイト~
E-Commerceサイトと問い合わせフォーム
E-Commerceサイトでは、情報を提供するだけでなく、顧客側からの情報のフィードバックも重要になってくる。
CGI(Common Gateway Interface)
メール送信フォーム
そうしたインタラクティブなページを実現するための方法には、大きく分けて、サーバ側で処理を行うものとクライアント側で処理を行うものとがある。前者の代表的なものがCGI(Common Gateway Interface)と呼ばれる仕組みだ。今回は、ユーザーがWebページ上の空欄を埋めて送信ボタンを押すだけでメールが送れるメール送信フォーム(フォーム・メールともいう)を例にとって、CGIの利用法や注意点を解説する。
掲示板や検索エンジン
CGIを利用して実現
CGIを利用して実現しているインターネット上のサービスには、掲示板や検索エンジンなどがある。共通しているのは、Webブラウザで入力したデータに応じて、表示されるページが変化する点だ。
CGIとは
ユーザーが入力したデータを処理
具体的に何が起きているかを手順を追って見てみよう。まず、ユーザーがWebブラウザ上のフォームで、文字の記入やチェック・ボックスによる選択などの方法でデータを入力する。Webブラウザは、HTMLファイルで指定されているサーバ側のCGIプログラムに対して、ユーザーが入力したデータを送る。CGIプログラムは、そのデータに基づいて一定の処理を行う。例えば、掲示板なら新しい発言の追加、検索エンジンならデータベースの検索といった処理だ。CGIプログラムがその結果をHTML形式で生成し、Webブラウザに送信することで、ユーザーがその結果を知ることができる。この仕組み全体をCGIと呼ぶが、CGIプログラムを指してCGIと呼ぶこともある。
サーバ側処理を実現する技術
ASP(Active Server Pages)やPHP
ほかにサーバ側処理を実現する技術には、MicrosoftのWebサーバであるIISがサポートするASP(Active Server Pages)、オープンソースのWebサーバApacheがサポートするPHPなどがある。
定番のCGI
アクセス・カウンタやメール送信フォーム、掲示板
アクセス・カウンタやメール送信フォーム、掲示板といった定番のCGIは、プロバイダ側があらかじめプログラムを用意していることが多い。プロバイダが示す手順に沿って作業すれば、簡単に設置できるはずだ。標準CGIの欠点は、多くのユーザーがプログラムを共有するため動作が遅いこと。特にアクセス・カウンタの表示がタイム・アウトになって壊れているページはよく目にする。CGIを自前で用意すれば、このような問題はなくなる。また、プロバイダが用意していないタイプのCGIを使いたい場合もあるだろう。
独自CGIを使用したい場合
契約しているプロバイダを確認
ただし、ユーザーが用意したCGIプログラムの使用は許可していないプロバイダが多い。不注意なユーザーが処理の重いプログラムをWebサーバで動作させてしまうと、そのサーバを共用している他のユーザーに迷惑がかかるからだ。独自CGIを使用したい場合は、契約しているプロバイダが許可しているかどうかあらかじめ確認しておこう。
事実上のスタンダード「Perl」
CGIのプログラミング言語
CGIプログラムは、Webサーバ上で動作するものならどのような言語で記述しても構わない。しかし実際には、CGIプログラムの多くは「Perl」と呼ばれる言語で記述されている。Perlのほかには、C言語やUNIXのシェル・スクリプトなども使われる。
Perlが多用される理由
強力なテキスト処理能力
Perlが多用される理由はいくつかある。まず、強力なテキスト処理能力を持っている点。CGIでは文字列の検索や置換といったテキスト処理が中心になるためだ。また、コンパイルの必要がないスクリプト型の言語であるため、比較的取っ付きやすい。すでにPerlで記述されたCGIが数多く存在するため、そのコードを再利用することで新たなCGIを効率的に開発できるのも利点だ。文法的にはCと共通点が多いため、何らかの言語でプログラムを組んだ経験があれば、修得は容易だろう。Perlで記述されたCGIは、「ネットサーフレスキュー【Web裏技】」(http://www.rescue.ne.jp/)や「KENT WEB」(http://www.kent-web.com/)といったWebサイトで入手できる。
メール送信フォームの作り方
日本語の文字コードはEUCを使う
それでは、メール送信フォームを例に取って、CGIの使い方を見ていこう。
HTMLファイルとPerlで記述したCGIスクリプト
基本的には、入力部分を担当するHTMLファイルとPerlで記述したCGIスクリプトの二つのファイルを用意することになる。CGIスクリプトの拡張子は「cgi」にする。ファイル名は、それぞれ「form.htm」「form.cgi」とした。
日本語処理を行うライブラリ「jcode.pl」
プログラミングの手間を省く
今回はそれに加え、「cgi-lib.pl」「jcode.pl」という二つのライブラリ・ファイルを用いた。cgi-lib.plはCGIで必要な処理を行うライブラリ、jcode.plは日本語処理を行うライブラリで、いずれもPerlで記述されている。決まり切った処理はライブラリに任せることで、プログラミングの手間を省くことができる。cgi-lib.plはhttp://cgi-lib.berkeley.edu/、jcode.plはftp://ftp.iij.ad.jp/pub/IIJ/dist/utashiro/perl/で入手できる。
ライブラリとモジュール
拡張子pmの「モジュール」
ライブラリと同様の機能を提供するものに、pmという拡張子を持つ「モジュール」がある。モジュールは、オブジェクト指向を用いるためライブラリより強力だが、Perl5以降でないと使えない。ライブラリはPerl4でも使うことができる。Perl5以降はCGI.pmを標準で装備するので、cgi-lib.plを用意する必要はない。またjcode.plのモジュール版であるjcode.pmはhttp://openlab.ring.gr.jp/Jcode/index-j.htmlで入手できる。
文字コード
通常はシフトJISコード
実際にソース・コードを書く前に、注意しなければならないことがある。日本語を記述する際にどの文字コードを使うかという点だ。Windows上でソース・ファイルを作成すると、通常はシフトJISコードが用いられる。その場合は、HTMLファイル(form.htm)とCGIが生成するHTMLのコンテント・タイプの文字コードの指定を「Shift_JIS」としておけばいい。
シフトJISの注意点
ただし、シフトJISの中には、バックスラッシュ(¥)などPerlで特殊な意味を持つメタ文字のコードを含む漢字があり、そうした漢字を使うと文字化けしてしまう。例えば、シフトJISでは「能」は「945CH」で¥のコードである「5CH」を含むため、「能力」と表示させようとすると「迫ヘ」に文字化けする。これを防ぐには、該当する漢字の直後に「¥」を挿入してエスケープしたり、文字列をダブルクォート(")で囲んでいる場合は、メタ文字をそのまま表示するシングルクォート(')で囲むといった対策を講じなければならない。
文字コードを「EUC-JP」
この問題を回避するには、ソース・コードをEUCで記述し、文字コード指定を「EUC-JP」にしておけばいい。EUCの漢字コードはPerlのメタ文字を含まないからだ。「秀丸」などの高機能なテキスト・エディタを使えば、Windows上でもEUCを使うことができる。また、シフトJISコードをEUCに変換できるフリーのコンバータも数多く存在する。
FORMタグで入力ページを作る
入力用ページのHTMLファイル
ここから具体的にファイルの内容を解説していこう。入力用のページを実現するためのHTMLファイルがform.htmだ。ソース・コードをリスト1に示す。CGIスクリプトをEUCで記述する場合は、このファイルもEUCで記述してMETA要素のコンテント・タイプで文字コードにEUCを指定する。フォームのHTMLをシフトJISコードで記述しておき、CGIスクリプト側で変換することもできるが、複数の文字コードが混在するので処理が面倒になる。EUCに統一しておくのが無難だ。
FORM要素
入力フォームの部分はFORM要素としてタグで指定する。action属性は必須で、入力された値を渡すプログラムを指定する。今回用いたホスティング・プロバイダではCGIスクリプトは「cgi-bin」というディレクトリに入れることになっていたので、CGIスクリプトのファイル名「form.cgi」をパスを含めて記述した。
GETとPOST
method属性では、プログラムに対する値の渡し方(リクエスト・メソッドと呼ぶ)を指定する。具体的にはGETとPOSTという二つの方法がある。省略するとGETとみなされる。
GETでプログラムに渡されるデータ
GETではプログラムに渡されるデータはURLの「?」以降の部分に表示される(リスト2)。データは、後述する「URLエンコーディング」という方式に従って変換された形式になる。ユーザーがそのままブックマークすると、そのページを簡単に再現できるメリットがあるため、「Yahoo!」など多くの検索エンジンはこの方法を採用している。
送信できるデータの量が制限
ただし、GETでは環境変数を用いるため、送信できるデータの量が制限される。今回のようにデータ量が多くなる可能性がある場合は、URLとは別にデータを送信するPOSTを使う。
フォームの中のコントロール
INPUT要素
フォームの中のコントロールは、FORM要素の中のINPUT要素で表現する。INPUTは開始タグのみの要素で終了タグは付けない。type属性ではコントロールの種類を指定し、name属性でコントロールに付ける名前を指定する。この名前は、CGIスクリプトで入力データを扱う際の目印となる。
type属性
type属性でtextを指定すると1行の入力欄になり、radioなら排他選択のラジオ・ボタンになる。value属性では入力の初期値を指定する。ラジオ・ボタンではユーザーが文字を入力しないため、必ず指定しておく必要がある。checked属性を指定しておくと、最初から選択した状態にしておくことができる。ラジオ・ボタンではなく複数選択が可能なチェック・ボックスにしたい場合は、type属性でradioの代わりにcheckboxにすればいい。
複数行の入力欄
複数行の入力欄を作る場合は、INPUT要素ではなくTEXTAREA要素を用いる。この要素は終了タグが必要だ。cols属性で横の文字数、rows属性で縦の文字数を指定する。
typeをsubmit
INPUT要素でtypeをsubmitにすると入力の確定ボタン、resetにするとリセット・ボタンになる。value属性で値を指定した場合は、ボタンに表示される名前になる。
元の日本語にデコードする
CGIの処理を行うform.cgi
次にCGIの処理を行うform.cgiのソース・コードを説明していこう(リスト3)。このCGIスクリプトの主な役割は、form.htmから渡されたデータをデコードして、「sendmail」というメールを送信するUNIXのコマンドに渡すことだ。そのため「メール・デコード」とも呼ばれる。
サーバ上のPerlのパス
1行目には、サーバ上のPerlのパスを記述する。パスはサーバによって異なるので、プロバイダに確認しておこう。Telnetが使える場合は「which perl」と入力することで知ることができる。
require関数
requireは、ライブラリの機能をスクリプトに組み込むための関数。ライブラリは実際にはサブルーチンの集合体になっており、requireで組み込んでおけば、「&」の後にサブルーチン名を指定して呼び出すことにより、ライブラリの機能を用いることができる。
サブルーチンを呼び出す
次の「&ReadParse」で、cgi-lib.plのReadParseというサブルーチンを呼び出している。これは、form.htmでユーザー入力したデータを受け取り、値を取り出して、デコードするという一連の処理を行うものだ。具体的に見ていこう。
URLエンコーディング
form.htmの<INPUT type="text" name="shimei">で「日経 太郎」と入力した場合、まず「日経 太郎」がURLエンコーディングという規則に従って変換される。URLエンコーディングでは、英数字およびアスタリスク(*)など5個の記号は変換されない。半角スペースはプラス(+)に変換される。その他の文字は「%」+16進数の文字コードで表現される。2バイトの日本語コードは1バイトずつ変換される。例えば、EUCで「日経 太郎」と入力した場合は「%C6%FC%B7%D0+%C2%C0%CF%BA」になる。次にname属性で指定した「shimei」と「=」が前に付き、最終的に「shimei=%C6%FC%B7%D0+%C2%C0%CF%BA」という値がform.cgiに渡される。
ReadParseサブルーチン
ReadParseは、まずURLデコードを行って「日経 太郎」という値を取り出し、それを「$in("shimei")」という名前の連想配列(ハッシュともいう)に格納する。$で始まる文字列はPerlでは変数として扱われる。その後に[]が付くと配列とみなされ、$animal[2]といった形式になる。連想配列は、添え字として数字の代わりに文字列を使える配列のことで、[]の代わりに{}を用い、$animal{"dog"}といった形式になる。ReadParseは、formで入力された値を$in{"コントロールの名前"}という連想配列に格納してくれるのである。
CGIに適したヒアドキュメント
テキスト・データを見た目通りに記述
$mailbodyという変数はメール全体のテキストを格納するために用いた。値はPerlの「ヒアドキュメント」という機能を使って記述している。ヒアドキュメントを使うと、まとまった量のテキスト・データを見た目通りに記述できるので、HTMLを生成する必要があるCGIでは重宝だ。通常の文字列のように改行ごとにいちいち「¥n」と記述する必要がなく、特にこのソース・コードの最後にあるようにprint文でHTMLを出力する際には、1行ごとに「print」と記述する手間を省くことができる。
ヒアドキュメントの注意点
ヒアドキュメントは、「<<」の後に任意の文字列(ここではEnd of Messageの意味でEOMを使った)を指定し、テキストの終わりに同じ文字列を記述することで使うことができる。中に変数名を記述した場合はその値に置き換えられる。変数の展開を行いたくないない場合は、最初を<<'EOM'などと「'」で囲む。注意しなければならないのは、<<と文字列の間には空白を入れないこと。<<の後に空白を入れると次の空行がテキストの終わりとみなされるため、誤動作の原因になる。また、終わりを示す文字列の行には他の文字は一切記述してはならず、その行がファイルの終わりになっている場合には必ず改行する必要がある。
メールヘッダの記述方法
ここに記した$mailbodyの内容のうち、最初の2行はヘッダに相当している。ヘッダと本文の間には必ず空行を入れること。「Subject:」の後の文字列がメールのタイトルになる。「Reply-To:」の後に記述するアドレスは、このメールに対して返信した場合の送り先のアドレスとなる。sendmailでメールを送ると、差出人のアドレスは「xxxx@プロバイダのサーバ名」(xxxxはプロバイダによって異なる)になるため、本来の差出人に返信するには、いちいちメール・アドレスを書き換えなければならない。「Reply-To:」で指定しておけば、メーラが自動的にそのアドレスを返信アドレスとみなしてくれる。
jcode'convertサブルーチン
jcode'convertは、jcode.plの中に記述されているサブルーチンで、文字コードの変換を行う。ここでは、EUCで記述されたメールの内容を7ビットのJISコード(ISO-2022-JP)に変換している。メールの送信プロトコルであるSMTPは7ビットのコードしか扱えないためだ。
メールの送信作業
次の部分でメールの送信作業を行っている。openはファイルをオープンしてファイル・ハンドルに結びつける関数。ファイル・ハンドル名、オープンするファイル名の順に指定する。オープンするファイル名の前にパイプ(|)があると、そのファイル名はコマンドと解釈され、ファイル・ハンドルに対する出力はそのコマンドに引数として渡される。
sendmailコマンド
sendmailはメールを送信する機能を持つUNIXのコマンドだ。perlと同様にパスを調べ、パスを含めて記述する。プロバイダによっては、セキュリティ上の問題からsendmailの使用を許可していない場合があるので、使えるかどうかは前もって確認しておこう。
sendmailの仕様
ここでは、まずMAILというファイル・ハンドルをパイプを使ってsendmailに接続している。次にprint関数でMAILに対して$mailbodyの内容を出力することにより、sendmailにメールの本文を渡す。MAILをクローズするとメールの内容が終わったと判断され、sendmailが実際にメールを送信する。ここに記述するメール・アドレスは、メールを受け取りたいアドレスにしておく。
メール送信完了
ユーザーに知らせるページを表示
メールの送信が終わったら、それをユーザーに知らせるためのページを表示する。print関数でヒアドキュメントを使ってHTMLを出力している。
コンテント・タイプを指定するヘッダ
最初の行にあるのが、コンテント・タイプを指定するヘッダ(HTMLの中のヘッダとは別のもの)だ。メールと同じく、ヘッダ行の後には必ず空行を入れる。通常のHTMLファイルは「htm」や「html」といった拡張子を用いることで、HTMLで記述されていることをブラウザに伝えることができるが、CGIで生成するHTMLでは拡張子を用いることができない。そのため、ブラウザで正しく表示させるためには、最初にコンテント・タイプとして「text/html」を指定してHTMLであることを明示する必要があるのだ。
FTPでサーバへ転送
フリーのクライアント・ソフト「FFFTP」
form.htm、form.cgi、cgi-lib.pl、jcode.plの4つのファイルが用意できたら、FTPを使ってプロバイダ側のサーバに転送しよう。CGIを入れるディレクトリはプロバイダによって決まっている場合が多いので、プロバイダの指示に従う。CGIスクリプトとライブラリのファイルは、ASCIIモードで転送すること。Windowsでは改行コードがCR+LFなのに対し、UNIXではLFのため、バイナリ・モードでそのまま送ってしまうと正しく動作しない。ASCIIモードだとCR+LFを自動的にLFに変換してくれる。なお、4月号で紹介したフリーのクライアント・ソフト「FFFTP」では、拡張子が「cgi」や「pl」のファイルはASCIIモードで送信するようデフォルトで設定されている。
パーミションの変更が必要
最後にもう一つ作業が残っている。「パーミションの変更」だ。パーミションとは「許可」という意味。UNIXでは複数のユーザーが一つのコンピュータを使うことを前提としているため、ファイルやディレクトリごとに誰がどのようにアクセスできるかが設定されている。具体的には、ファイルの所有者、所有者が属するグループ、第三者のそれぞれに対し、読む許可、書き換える許可、実行する許可を与えるかどうかが設定されている。つまり、一つのファイル(あるいはディレクトリ)には必ず9つのパーミションの設定がある。
「実行する許可」を与える
ファイルをFTPで転送しただけの状態では、「実行する許可」は誰にも与えられていない。CGIスクリプトを実行させるためには、所有者と第三者に実行権限を与える必要がある。高機能なFTPクライアント・ソフトはFTPを通してパーミションを変更する機能を持っている。Telnetが使える場合は、chmodというUNIXのコマンドを使って直接パーミションを変更する方法もある。なおライブラリには実行権限は要らないので、今回の場合、パーミションを変更するのはform.cgiだけでいい。
CGIがうまく動作しない場合のチェック項目
初心者が陥りやすいミス
最後に、初心者が陥りやすいミスをまとめた。もしCGIがうまく動作しない場合は、これらの項目をチェックしてみてほしい。
CGIの処理の大まかな流れ
ブラウザ上に表示されたメール送信フォームからメールを送信する
リスト1 左の入力ページを実現するためのform.htmのソース・コード
<HTML>
<HEAD>
<TITLE>メール送信フォームのサンプル</TITLE>
<META http-equiv="Content-Type" content="text/html; charset=EUC-JP">
</HEAD>
<BODY>
お問い合わせのメールはこちらからお願いします。
<FORM action="/cgi-bin/form.cgi" method="POST">
お名前<BR>
<INPUT type="text" name="shimei">
<INPUT type="radio" name="seibetsu" value="男性" checked>男性
<INPUT type="radio" name="seibetsu" value="女性">女性<BR>
メール・アドレス<BR>
<INPUT type="text" name="address" size=35><BR>
メール本文<BR>
<TEXTAREA name="honbun" cols=50 rows=5></TEXTAREA><BR>
<INPUT type="submit" value="送信">
<INPUT type="reset" value="リセット">
</FORM>
</BODY>
</HTML>
リスト2 Yahoo! JAPANで「日経バイト」というキーワードで検索した場合のURL。「?」以降に、入力したデータをURLエンコードした内容が表示されている
http://search.yahoo.co.jp/bin/search?p=%C6%FC%B7%D0%A5%D0%A5%A4%A5%C8
リスト3 form.cgiのソース・コード
#!/usr/local/bin/perl
require 'cgi-lib.pl';
require 'jcode.pl';
&ReadParse;
$mailbody = <<EOM;
Subject: MAIL from CGI
Reply-To: $in['address']
$in['shimei']さんからのメールです。
性別 $in['seibetsu']
メール・アドレス $in['address']
$in['honbun']
EOM
&jcode'convert(*mailbody, 'jis');
open(MAIL, '|/usr/bin/sendmail webmaster@nikkeibyte.co.jp');
print MAIL $mailbody;
close(MAIL);
print <<EOM;
Content-type: text/html; charset=EUC-JP
<HTML>
<HEAD>
<TITLE>送信完了</TITLE>
</HEAD>
<BODY>
メールを送信しました。
</BODY>
</HTML>
EOM
手軽だが使用には注意を要するSSI
CGIがページ全体を生成するのに対し、もっと手軽にページの一部分だけの表示を変えたい場合に用いられるのが、SSI(Server Side Include)と呼ばれる仕組みだ。HTMLファイルの中に1~2行記述しておくだけで、現在の日時やファイルの最終更新日時を表示することができる。また、ヘッダやフッタのファイルを自分で用意しておき、表示させたいページにSSIで読み込ませたりすることができる。プログラムを実行させることもできるので、ユーザーが用意したアクセス・カウンタはSSIを使って表示させることが多い。
セキュリティ・ホールの可能性
ただし、CGIの使用は許可していてもSSIの使用は許可していないプロバイダは多い。たとえSSIを許可していても、プログラムを実行するコマンドである「exec」だけは使えないように設定していたり、execで実行できるプログラムのディレクトリを制限している場合もある。任意のプログラムを実行できると、セキュリティ・ホールになる可能性があるからだ。
NCSA HTTPd搭載の機能
SSIは、Mosaicの開発元として知られる米Illinois大学のNCSA(National Center for Supercomputing Applications)が開発したWebサーバであるNCSA HTTPdが搭載している機能。NCSA HTTPdを基に開発され、現在最も広く用いられているApacheにも引き継がれている。
HTMLファイルの拡張子「shtml」
SSIを使用する場合は、HTMLファイルの拡張子を「shtml」といった異なるものに変えなければならない場合が多い。拡張子が「htm」「html」のファイルでSSIを実行できるよう許可すると、サーバ側ですべてのHTMLファイルでSSIを処理するかどうかチェックしなければならなくなり、負荷が大きくなるからだ。
SSIの書式
SSIに対応したWebサーバ
実際に使用する際には、リストAに示した書式で記述する。SSIに対応したWebサーバはこの部分を実行結果に置き換えたHTMLファイルをブラウザに送信する。HTMLのコメントの形式なので、SSIに非対応のWebサーバはこの部分を無視する。
リストA SSIを記述する際の書式
<!--#command tag="value"-->