CGI とは Common Gateway Interface の略で、データベースにアクセスしブラウザに表示させるためにサーバーに置くプログラムのことを指します。多くは perl というプログラム言語で書きます。perl はどの Linux ディストリビューションには標準でついてきますし、入門書も多いので勉強しやすいと言われています。perl はスクリプトを書くだけで、コンパイルなしでプログラムを動かすことができ、プラットフォームを越えて使うことができるので人気があります。
私は perl はほんの少しかじっただけですので、詳しい解説はできませんが、ゲストブックを例題に、CGI の実際をレポートしたいと思います。
私のゲストブックの HTML ソース(guest.html)は次の通りです。
<HTML> <HEAD> <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=EUC-JP"> <TITLE>Penguin Club-Guest Book </TITLE> </HEAD> <BODY BACKGROUND="./images/crc-back.gif"> <CENTER><IMG SRC="./images/penguin_club.gif" HEIGHT=60 WIDTH=292></CENTER> <UL> <UL> <center> <h2>Guest Book</h2> <P><A href="guestsee.cgi">ゲストブックを見る</A> <A href="index.html">ホームページに戻る</A></P> <blockquote> Penguin Club においでいただきありがとうございました。よろしかったら、コメントをお残しください。<BR> </blockquote> <HR> <FORM action="guestadd.cgi" method="POST"> <table> <tr><td>お名前</td><td><INPUT name="myname" size="40"></td></tr> <tr><td>メールアドレス</td><td><INPUT name="mymail" size="40"></td></tr> <tr><td colspan="2">コメントをお願いします(200文字以内−このボックス一杯に書けます)<BR> <TEXTAREA name="mycomment" rows="4" cols="50"></TEXTAREA><br><br> <center><INPUT type="submit" value=" 書 込 "> <INPUT type="reset" value=" 取 消 "></center> </table> </FORM> </center> <HR> </BODY> </HTML>ゲストブックに書き込んでもらいたいのは「名前」「メールアドレス」「コメント」の三つにし、それぞれ myname mymail mycomment という名前をつけました。myname と mymail は 一行だけのボックスで 40字にしました。コメントは 50字 × 4行にし、スクロールなしで 200文字としました。
<tr><td>お名前</td><td><INPUT name="myname" size="40"></td></tr> <tr><td>メールアドレス</td><td><INPUT name="mymail" size="40"></td></tr> <tr><td colspan="2">コメントをお願いします(200文字以内−このボックス一杯に書けます)<BR> <TEXTAREA name="mycomment" rows="4" cols="50"></TEXTAREA><br><br>これらのデータを submit ボタンを押すと guestadd.cgi で処理するよう、次のように action を定義しました。
<FORM action="guestadd.cgi" method="POST">
guestadd.cgi は perl で書きます。ゲストブックのデータは書き込みの時点でフォーマットを整えておくようにし、それをプレインテキスト形式で guest.txt というデータファイルに保存するようにしました。この方がエントリーの表示を早くできるからです。
次は guestadd.cgi のメインルーチンです。
#!/usr/bin/perl
# 漢字ライブラリの設定
require "jcode.pl";
# 初期化
&init_form('euc');
# ヘッダの送出
print "Content-type: text/html\n\n";
# guest.html からの情報
$myname = $form{'myname'};
$mymail = $form{'mymail'};
$mycomment = $form{'mycomment'};
$datestr = &get_date_string;
# 名前が入力されているかどうかの確認
if ($myname eq '') {
&print_error("名前が入力されていません。");
}
# メールアドレスが入力されているかどうかの確認
if ($mymail eq '') {
&print_error("メールアドレスが入力されていません。");
}
# メールアドレスが正しいかどうかの確認
if ($mymail !~ '@') {
&print_error("メールアドレスが正確ではありません。");
}
# コメントの長さをチェック
if (length($mycomment) > 500) {
&print_error("コメントが制限字数を越えています。");
}
# タグを置換
$mycomment =~ s/</</g;
$myname =~ s/</</g;
# データファイルのオープン
if (!open(TXT, "+<guest.txt")) {
&print_error("ゲストブックのデータファイルが見つかりません。");
}
# データファイルのロック
if (!&lock_file(TXT)) {
close(TXT);
&print_error("書き込みの衝突が起きました。");
}
# これまでのデータファイルを全部読む
@txt = <TXT>;
# ファイルの先頭にまき戻す
seek(TXT, 0, 0);
# 新規データの書き込み
print TXT "<font color=\"#52188B\">■ <A HREF=mailto:$mymail>$myname</A>\n";
print TXT " [$datestr]</font><br>\n";
if ($mycomment ne '') {
print TXT "$mycomment<br>\n";
}
print TXT "<HR>\n";
# これまでのデータを書き加える
print TXT @txt;
# ファイルサイズを切りつめる
truncate(TXT, tell(TXT));
# データファイルのアンロック
&unlock_file(TXT);
# データファイルのクローズ
close(TXT);
# 書き込み成功
&page_top("ゲストブックへの書き込みありがとうございました");
&page_end;
# 終了
exit(0);
ここで中心的な部分は、
$myname = $form{'myname'};
$mymail = $form{'mymail'};
$mycomment = $form{'mycomment'};
$datestr = &get_date_string;
と
print TXT "<font color=\"#52188B\">■ <A HREF=mailto:$mymail>$myname</A>\n";
print TXT " [$datestr]</font><br>\n";
if ($mycomment ne '') {
print TXT "$mycomment<br>\n";
}
です。guest.html から発信された myname mymail my comment
を受け取って、guest.txt
に書きこんでいます。書き込み時間も自動的に記入するように
&get_date_string というサブルーチンも使っています。
次にメインルーチンをすこしづつ解説します。
#!/usr/bin/perlこれはこのファイルを「perl で動かせ」という命令です。guestadd.cgi はプログラムファイルですから、このファイルを作成したら、実行属性をつけなければなりません。
# 漢字ライブラリの設定 require "jcode.pl";perl で日本語を扱うためには jcode.pl が必要です。jcode.pl は適当なダウンロードサイトから手に入れておきます。
# 初期化
&init_form('euc');
これは次のサブルーチンで処理させます。&init_form の引数 'euc'
は文字コードで、サブルーチンでは $charcode に価を引き渡しています。
sub init_form {
local($query, @assocarray, $assoc, $property, $value, $charcode, $method);
$charcode = $_[0];
$method = $ENV{'REQUEST_METHOD'};
$method =~ tr/A-Z/a-z/;
if ($method eq 'post') {
read(STDIN, $query, $ENV{'CONTENT_LENGTH'});
}
else {
$query = $ENV{'QUERY_STRING'};
}
@assocarray = split(/&/, $query);
foreach $assoc (@assocarray) {
($property, $value) = split(/=/, $assoc);
$value =~ tr/+/ /;
$value =~ s/%([A-Fa-f0-9][A-Fa-f0-9])/pack("C", hex($1))/eg;
&jcode'convert(*value, $charcode);
$form{$property} = $value;
}
}
次は、ブラウザに文字列を送るためのきまり文句です。
# ヘッダの送出 print "Content-type: text/html\n\n";guest.html からの情報 $myname $maymail $mycomment は次のようにしてチェックをかけています。コメントに < や > などが含まれているとブラウザに正しく表示されませんので、それらを取り除く作業もここでしています。エラーが出たら &print_error に表示させるようにしてあります。
# 名前が入力されているかどうかの確認
if ($myname eq '') {
&print_error("名前が入力されていません。");
}
# メールアドレスが入力されているかどうかの確認
if ($mymail eq '') {
&print_error("メールアドレスが入力されていません。");
}
# メールアドレスが正しいかどうかの確認
if ($mymail !~ '@') {
&print_error("メールアドレスが正確ではありません。");
}
# コメントの長さをチェック
if (length($mycomment) > 500) {
&print_error("コメントが制限字数を越えています。");
}
# タグを置換
$mycomment =~ s/</</g;
$myname =~ s/</</g;
さて、データファイルのオープンですが、ここでは読み書き両方できるようにオープンします。
# データファイルのオープン
if (!open(TXT, "+<guest.txt")) {
&print_error("ゲストブックのデータファイルが見つかりません。");
}
データファイルには同時に何人かがアクセスしようとしますので、それを避けるためにロックをかけます。ロックをかけられなかった場合はエラーを表示し、やりなおしてもらいます。
# データファイルのロック
if (!&lock_file(TXT)) {
close(TXT);
&print_error("書き込みの衝突が起きました。");
}
ゲストブックでは新しいデータを先頭に載せるため、まずデータ全部を読みこんで、それから書き込みポイントを先頭に持ってきます。それが次のラインです。
# これまでのデータファイルを全部読む @txt = <TXT>; # ファイルの先頭にまき戻す seek(TXT, 0, 0);新規データを書き込んだら、これまでのデータを付け加え、ファイルの不要な部分を切り捨て、ファイルのロックを外してから、閉じます。次がその一連の作業部分です。
# これまでのデータを書き加える print TXT @txt; # ファイルサイズを切りつめる truncate(TXT, tell(TXT)); # データファイルのアンロック &unlock_file(TXT); # データファイルのクローズ close(TXT);書き込みが成功したら、それを伝えたほうが親切なので、次のようなメッセージを表示して終了するようにしました。
# 書き込み成功
&page_top("ゲストブックへの書き込みありがとうございました");
&page_end;
# 終了
exit(0);
次は表示関係のサブルーチンです。
# エラー表示
# &print_error("メッセージ");
sub print_error {
local($msg) = @_;
&page_begin($msg);
print "ブラウザの「戻る」アイコンを押してもとに戻り、";
print "記入、または訂正してください。うまくいかない時は";
print "<A HREF=\"mailto:webmaster\@penguinclub.net\">webmaster\@penguinclub.net</A>";
print " までお知らせください。\n";
&page_end;
exit(0);
}
# ページのはじめ
# &page_top("メッセージ");
sub page_begin {
local ($msg) = @_;
print "<HTML>\n";
print "<HEAD>\n";
print "<TITLE>$msg</TITLE>\n";
print "</HEAD>\n";
print "<BODY>";
print "<H1>$msg</H1>\n";
}
# ページの終わり
sub page_end {
print "<HR>\n";
print "<A HREF=\"guestsee.cgi\">ゲストブックを見る</A>\n";
print " ";
print "<A HREF=\"http://localhost/~penguin/guest.html\">ゲストブックに記入する</A>\n";
print " ";
print "<A HREF=\"http://localhost/~penguin/index.html\">ホームページに戻る</A>\n";
print "<HR>\n";
print "</BODY>\n";
print "</HTML>\n";
}
次は時刻を得てそれを文字列にするサブルーチンです。9/11/01 8:05AM
のように表示させるようにしました。
# 現在日時を文字列にする
sub get_date_string {
local($sec, $min, $hour, $day, $mon, $year);
( $sec, $min, $hour, $day, $mon, $year ) = localtime(time);
$year -= 100;
$mon++;
$ampm = "AM";
if ($hour == 12) {
$ampm = "PM";
}
if ($hour > 12) {
$hour -= 12;
$ampm = "PM";
}
# 必要なら 0 を付加する
if ($year < 10) {
$year = "0$year";
}10/2/01
if ($min < 10) {
$min = "0$min";
}
return "$mon/$day/$year $hour:$min$ampm";
}
データファイルのロックは次のようになっています。
# データファイルのロック
sub lock_file {
local(*FILE) = @_;
eval("flock(FILE, 2)"); # 2=LOCK_EX
if ($@) {
return 0;
}
return 1;
}
# データファイルのアンロック
sub unlock_file {
local(*FILE) = @_;
eval("flock(FILE, 8)"); # 8=LOCK_UN
}
上記のソースコードをメインルーチン、サブルーチンの順にならべて guestadd.cgi として保存すれば、CGI の出来上がりとなります。