#!/usr/bin/perl -w use DBI; use CGI; use Encode; # MySQLのmy_dbデータベースのbbsテーブルの構成 # --------------------------- # フィールド 属性 # id INT NOT NULL AUTO_INCREMENT PRIMARY KEY # name VARCHAR(16) NOT NULL # message VARCHAR(200) NOT NULL # date TIMESTAMP(14) # データベースの認証設定 # --------------------------- my ($dsn, $user, $password); $dsn = "DBI:mysql:my_db:localhost"; $user = "foo"; $password = "password"; # $nameと$messageの制限文字数 # --------------------------- my ($max_name, $max_message); $max_name = 16; $max_message = 200; # フォームから値を取り出す # --------------------------- my $cgi = new CGI; my ($name, $message, $mode); if ($cgi->param('mode')) { $mode = $cgi->param('mode'); # モード $name = $cgi->param('name'); # 名前欄の値 $message = $cgi->param('message'); # メッセージ欄の値 } else { $mode = ""; $name = ""; $message = ""; } # 各モードの処理 # --------------------------- # $modeが空ならば、一覧モード if (!$mode) { &get_header; # HTMLヘッダーを出力 &get_form; # 入力フォームを出力 &get_db; # DBから取得したデータをHTMLに生成して出力 &get_footer; # HTMLフッターを出力 } # 確認モード elsif ($mode eq "confirm") { &get_header; # 制限文字数とタグのチェック my $error_log = ""; if (!&check_var(\$name, \$message, \$error_log)) { &get_confirm; # 確認画面を出力 } else { &get_error($error_log); # エラー表示を出力 &get_form; } &get_footer; } # 修正モード elsif ($mode eq "modify") { &get_header; my $error_log = ""; if (!&check_var(\$name, \$message, \$error_log)) { &replace_br(\$message); # エスケープされた
を改行に置換 &get_form; } else { &get_error($error_log); &replace_br(\$message); &get_form; } &get_footer; } # 書き込みモード elsif ($mode eq "write") { my $error_log = ""; if (!&check_var(\$name, \$message, \$error_log)) { &write_db; } else { &get_header; &get_error($error_log); &get_footer; } } # 正常なモード以外のアクセス else { &get_header; &get_error("

不正なアクセスです。

"); &get_footer; } # サブルーチン # --------------------------- # 文字数制限とタグのエスケープ sub check_var { my ($nm, $nm_restore, $msg, $msg_restore, $err_log, $err); $nm = $_[0]; # $nameのreference $msg = $_[1]; # $messageのreference $err_log = $_[2]; # $error_logのreference $nm_restore = $$nm; &restore_tag(\$nm_restore); $msg_restore = $$msg; &restore_tag(\$msg_restore); $nm_length = length(decode('utf-8', $nm_restore)); $msg_length = length(decode('utf-8', $msg_restore)); # $nameを処理する if ($nm_length == 0) { ${$err_log} .= "

名前を記入してください。

\n"; $err += 1; } elsif ($nm_length > $max_name) { ${$err_log} .= "

名前が制限文字数を超えています。

\n"; ${$nm} = ""; $err += 1; } else { &delete_crlf($nm); # 改行を許可しない &escape_tag($nm); } # $messageを処理する if ($msg_length == 0) { ${$err_log} .= "

メッセージを記入してください。

\n"; $err += 1; } elsif ($msg_length > $max_message) { ${$err_log} .= "

メッセージが制限文字数を超えています。

\n"; ${$msg} = ""; $err += 1; } else { &escape_tag($msg); &replace_crlf($msg); } return $err; } # &、<、>、"を実体参照に置換 sub escape_tag { my $var = $_[0]; # reference ${$var} =~ s/&/&/g; ${$var} =~ s//>/g; ${$var} =~ s/"/"/g; } # 実体参照を元に戻す sub restore_tag { my $var = $_[0]; # reference ${$var} =~ s/&/&/g; ${$var} =~ s/<//g; ${$var} =~ s/"/"/g; } # 改行文字を
に置換 sub replace_crlf { my $var = $_[0]; # reference ${$var} =~ s/\x0D\x0A/
/g; ${$var} =~ s/\x0D/
/g; ${$var} =~ s/\x0A/
/g; } #
だけをエスケープする際に使用 sub escape_br { my $var = $_[0]; # reference ${$var} =~ s/
/<br \/>/g; } # ダブルクォートをエスケープ sub escape_quote { my $var = $_[0]; # reference ${$var} =~ s/"/\\"/g; } # バックスラッシュをエスケープ sub escape_backslash { my $var = $_[0]; # reference ${$var} =~ s/\\/\\\\/g; } # エスケープされた
を元に戻す sub restore_br { my $var = $_[0]; # reference ${$var} =~ s/<br \/>/
/g; } # エスケープされた
を改行文字に置換 sub replace_br { my $var = $_[0]; # reference ${$var} =~ s/<br \/>/\n/g; } # 改行文字を除去 sub delete_crlf { my $var = $_[0]; # reference ${$var} =~ s/\x0D\x0A//g; ${$var} =~ s/\x0D//g; ${$var} =~ s/\x0A//g; } # フォームのHTMLを出力 sub get_form { print <<"HTML";
Name
Message
HTML } # 確認画面のHTMLを出力 sub get_confirm { my ($name_value, $message_value); $name_value = $name; $message_value = $message; &escape_br(\$message_value); print <<"HTML";

確認

以下の内容でよろしいでしょうか?

$name

$message

HTML } # データベースへの書き込み sub write_db { my $dbh = DBI->connect($dsn, $user, $password); if (!$dbh) { &get_error("

データベースに接続できません。

"); return; } &restore_tag(\$name); &escape_backslash(\$name); &escape_quote(\$name); &replace_br(\$message); &restore_tag(\$message); &escape_backslash(\$message); &escape_quote(\$message); $dbh->do("SET NAMES utf8"); my $query = "INSERT INTO bbs SET name=\"" . $name . "\", message=\"" . $message . "\""; $dbh->do($query); $dbh->disconnect(); print $cgi->redirect('/bbs/bbs.pl'); } # データベースからの読み出し sub get_db { my $dbh = DBI->connect($dsn, $user, $password); if (!$dbh) { &get_error("

データベースに接続できません。

"); return; } my $rows = $dbh->selectrow_array("SELECT COUNT(*) FROM bbs"); if(!$rows) { &get_error("

データベースが空です。

"); return; } else { $dbh->do("SET NAMES utf8"); my ($query, $sth, @row); $query = "SELECT id, name, message, date FROM bbs ORDER BY id DESC"; $sth = $dbh->prepare($query); $sth->execute(); while(@row = $sth->fetchrow_array) { my ($name, $message, $date); $name = $row[1]; $message = $row[2]; $date = $row[3]; &escape_tag(\$name); &escape_tag(\$message); &replace_crlf(\$message); print <<"HTML";

$name

$message

$date
HTML } $sth->finish(); } $dbh->disconnect(); } # エラー表示用のHTMLを出力 sub get_error { print <<"HTML"

エラー

$_[0]
HTML } # HTMLヘッダーを出力 sub get_header { print <<"HTML"; Content-Type:text/html Poturi : BBS

Poturi

HTML } # HTMLフッターを出力 sub get_footer { print <<"HTML"; HTML }