CGI の応用編として、CGI による掲示板を実際に作っていきましょう。作成するのは次の 2つのファイルです。
ファイル | 説明 |
---|---|
bbs.cgi | 掲示板の CGI 本体です。内容は下記で説明します。通常の CGI の設置方法に従って設置してください。 |
bbs.txt | 掲示板に書き込まれたメッセージを記録するファイルです。最初は中身は空でかまいません。bbs.txt と同じフォルダに設置し、CGI実行ユーザ が書き込み可能(下記参照)となるように設定してください。 |
UNIX系 OS で、CGI が nobody などの権限で動作する場合は、bbs.txt の パーミッション を 666(rw-rw-rw-)にしてください。CGI が自分自身の権限で動作する場合は、644(rw-r--r--)にしてください。Windows NT系 + IIS の場合は IUSER_マシン名 のユーザに対して書き込み権を設定してください。Windows 95系の場合は、特に設定は必要ありません。
bbs.cgi の内容を次のように作成してください。
#!/usr/bin/perl require "jcode.pl"; # 漢字コード変換ライブラリを読み込む(→ jcode.pl) # 漢字コード # (EUCにする場合は、$charset="euc-jp" と $kcode="euc" を設定) $charset = "Shift_JIS"; $kcode = "sjis"; # 現在の時刻($time)を求めておく ($sec, $min, $hour, $mday, $mon, $year) = localtime(); $time = sprintf("%04d/%02d/%02d %02d:%02d:%02d", $year + 1900, $mon + 1, $mday, $hour, $min, $sec); # フォームデータを読み込む(→ CGIでフォームに・・・) read(STDIN, $buf, $ENV{'CONTENT_LENGTH'}); foreach (split(/&/, $buf)) { ($name, $value) = split(/=/); $value =~ tr/+/ /; $value =~ s/%([0-9a-fA-F][0-9a-fA-F])/pack("C", hex($1))/eg; $value =~ s/&/&/g; $value =~ s/</</g; $value =~ s/>/>/g; $value =~ s/"/"/g; jcode::convert(*value, $kcode); # 漢字コードを変換しておく $FORM{$name} = $value; } # メッセージの改行を <br> に置換する $FORM{'MESG'} =~ s/(\r\n|\n|\r)/<br>\n/g; # ファイルに追加書き込みする if ($FORM{'MESG'} ne "") { $msg = "<hr>\n" . "<div class=header>\n" . "<span class=name>$FORM{'NAME'}</span>\n" . "<span class=time>$time</span>\n" . "</div>\n" . "<div class=mesg>$FORM{'MESG'}</div>\n"; open(OUT, ">> bbs.txt") || ErrorExit("書き込み失敗。"); print OUT $msg || ErrorExit("書き込み失敗。"); close(OUT) || ErrorExit("書き込み失敗。"); }
# ファイルの内容を読みこむ open(IN, "bbs.txt"); @body = <IN>; close(IN); # 結果を書き出す print <<END_OF_DATA; Content-type: text/html <html> <head> <meta http-equiv="Content-type" content="text/html; charset=$charset"> <title>簡易掲示板</title> <style type="text/css"> <!-- .name { color: #ff0000; font-weight: bold; } .time { color: #666666; } .mesg { color: #666600; } --> </style> </head> <body> <h3>★★ 簡易掲示板 ★★</h3> <form method="POST" action="$ENV{'SCRIPT_NAME'}"> <div>名前:<input type="text" name="NAME"></div> <div><textarea name="MESG" cols=40 rows=6></textarea></div> <div><input type="submit" value="送信"></div> </form> @body <hr> </body> </html> END_OF_DATA
メッセージは書きこんだ順に下側に追加されていきますが、これを、新しいものほど上に表示させるようにするには、bbs.cgi の「# ファイルに追加書き込みする」の個所を上記のものに置き換えてください。
# ファイルに追加書き込みする if ($FORM{'MESG'} ne "") { open(FILE, "+< bbs.txt") || ErrorExit("書き込み失敗"); @lines = <FILE>; unshift(@lines, "<hr>\n", "<div class=header>\n", "<span class=name>$FORM{'NAME'}</span>\n", "<span class=time>$time</span>\n", "</div>\n", "<div class=mesg>$FORM{'MESG'}</div>\n"); seek(FILE, 0, 0); print(FILE @lines) || ErrorExit("書き込み失敗"); close(FILE) || ErrorExit("書き込み失敗"); }
Perl ではファイルの先頭に追記するという手段は無いので、ファイルの内容を配列としてすべて読み取り、その先頭に新メッセージを追加し、再度ファイルにすべて書きこむことにより実現しています。
ブラウザで再表示を行うと、書き込みが二重に投稿されてしまうことがあります。これを防ぐには、「# ファイルの内容を読みこむ」の前に上記の記述を挿入してください。
# 二重投稿を抑制する if ($FORM{'MESG'} ne "") { print "Location: http://$ENV{'HTTP_HOST'}$ENV{'SCRIPT_NAME'}\n\n"; exit(0); }
CGI の Content-type ヘッダの代わりに Location ヘッダが書き出されると、Location で指定したページにジャンプします。ジャンプする際に、フォームに書きこんだデータを破棄するため、再読み込みしてもフォームデータが二重投稿されなくなります。