#!/usr/local/bin/perl

#┌─────────────────────────────────
#│  WEB ENQUETE v3.2 (2002/08/02)
#│  Copyright(C) Kent Web 2002
#│  webmaster@kent-web.com
#│  http://www.kent-web.com/
#└─────────────────────────────────
$ver = 'WEB ENQUETE v3.2';
#┌─────────────────────────────────
#│ [注意事項]
#│ 1. このスクリプトはフリーソフトです。このスクリプトを使用した
#│    いかなる損害に対して作者は一切の責任を負いません。
#│ 2. 設置に関する質問はサポート掲示板にお願いいたします。
#│    直接メールによる質問は一切お受けいたしておりません。
#└─────────────────────────────────
#
# 【ファイル構成例】
#
#  public_html (ホームディレクトリ)
#      |
#      +-- enq / enq.cgi   [755]
#           |    enq.log   [666]
#           |    jcode.pl  [644]
#           |    graph.gif
#           |
#           +-- lock [777] /
#
#
# 【チェックモード呼び出し例】
#
#  http://www.xxx.xxx/enq/enq.cgi?mode=check
#

#============#
#    設定    #
#============#

# ライブラリ取り込み
require './jcode.pl';

# タイトル名
$title = "相続税法［O−HARA］";

# タイトル文字の色
$t_color = "#1D7D7B";

# タイトル文字の大きさ (スタイルシートで反映)
$t_size = '18pt';

# 本文の文字大きさ (スタイルシートで反映)
$b_size = '10pt';

# 本文の文字タイプ (スタイルシートで反映)
$f_family = "MS UI Gothic";

# 投票結果テーブルの下地の色
$tbl_color = "#FFFFFF";

# スクリプト名
$script = './enq.cgi';

# ログファイル名
$logfile = './enq.log';

# 管理用パスワード
$pass = 'houjin1234';

# ユーザ項目追加機能（ユーザが自由に項目を追加可能）
#  0 : しない
#  1 : する（同一人物による連続した追加行為は禁止）
#  2 : する（同一人物による連続した追加行為は可能）
$freeitem = 0;

# グラフ画像 (絶対パスだと http://から)
$graph = "../../graph.gif";

# 戻り先 (絶対パスだと http://から)
$home = "../../index.htm";

# bodyタグ
$body = '<body background="" bgcolor="#FFFFFF" text="#000000" link="#0000FF" vlink="#800080" alink="#FF0000">';

# ロックファイル機構 (0=no 1=yes)
$lockkey = 1;

# ロックファイル名
$lockfile = './lock/enq.lock';

# 二重登録チェック (0=no 1=yes)
#  → IPアドレスによる重複チェック機能
$ip_check = 1;

# 投稿時は「method=POST」限定 (0=no 1=yes)
#  → セキュリティ対策
$postonly = 1;

# 他サイトから投稿排除時に指定 (http://から書く)
#  → セキュリティ対策
$myurl = "";

# 戻るボタン (0=no 1=yes)
#  → Javascriptによる戻るボタン表示機能
$BackButton = 1;

# サブタイトル
#  → タイトル下にサブタイトルを記述します。（タグ使用可）
$subtitle = <<'EOM';
<!-- ここから -->
<OL>
  <LI>このコーナーは、『相続税法』です。
  <LI>大原さんの解答速報に準拠したあなたの点数を教えてください！
  <LI>点数は100点満点でお願いします。
  <LI>since2012/08/〜<DIV style="HEIGHT: 0px; LEFT: -200px; POSITION: absolute; TOP: -200px; VISIBILITY: visible; WIDTH: 0px; Z-INDEX: 1"><IMG src="http://houjinzei.com/vicounter/s3cnt/w_s3ctup.cgi"></DIV></div>
</OL>
<!-- ここまで -->
EOM

# タイトル画像を使う場合 (http://から画像を指定)
$ImgT = "http://houjinzei.com/sozai/plert/norinori.gif";

# タイトル画像を使う場合に「横幅」「縦幅」をそれぞれピクセル数で記述
$ImgW = 100;
$ImgH = 30;

# タグ広告挿入オプション (FreeWebなど）
#   → <!-- 上部 --> <!-- 下部 --> の代わりに「広告タグ」を挿入する。
#   → 広告タグ以外に、MIDIタグやLimeCounter等のタグにも使用可能。
$banner1 = '<!-- 上部 -->';  # 掲示板上部に挿入
$banner2 = '<!-- 下部 -->';  # 掲示板下部に挿入

#============#
#  設定完了  #
#============#

# メイン処理
&decode;
if ($mode eq 'regist') { &regist; }
elsif ($mode eq 'admin') { &admin; }
elsif ($mode eq 'item' && $freeitem) { &make_item; }
elsif ($mode eq 'check') { &check; }
&html;

#------------------#
#  初期画面を表示  #
#------------------#
sub html {
	&header;

	if ($BackButton) {
		print "<form><input type=button value=\"戻る\" onClick=window.open(\"$home\",\"_self\")></form>\n";
	} else {
		print "<a href=\"$home\" target=\"_self\">▲TOP</a>\n";
	}

	print "<div align=center>\n";
	print "$banner1<P>\n" if ($banner1 ne "<!-- 上部 -->");

	# タイトル
	if ($ImgT) {
		print "<img src=\"$ImgT\" width=$ImgW height=$ImgH alt=\"$title\">\n";
	} else {
		print "<hr width=400 size=1>\n";
		print "<b style=\"font-size:$t_size;color:$t_color\">$title</b>\n";
		print "<hr width=400 size=1>\n";
	}
print "<br>- <a href='http://www.houjinzei.com/index.htm' target='_top'>HOME</a> -\n";
	print "<P><table><tr><td>$subtitle</td></tr></table>\n";

	open(IN,"$logfile") || &error("Open Error : $logfile");
	$top = <IN>;
	$total=0;
	while (<IN>) {
		($no,$item,$count) = split(/<>/);
		$no{$no}    = $no;
		$item{$no}  = $item;
		$count{$no} = $count;

		$total += $count;
	}
	close(IN);

	$all = &filler($total);
	print "<form action=\"$script\" method=\"POST\">\n";
	print "<input type=hidden name=mode value=\"regist\">\n";
	print "有効投票：<b>$all</b>件\n";
	print "<P><table border=2 cellpadding=2 cellspacing=0 bgcolor=\"$tbl_color\">\n";
	print "<tr><th>回答</th><th>点数</th><th>人数</th><th>割合</th></tr>\n";

	# 回答がすべて 0 のとき
	if ($total == 0) {
		open(IN,"$logfile") || &error("Open Error : $logfile");
		$top = <IN>;
		while (<IN>) {
			($no,$item,$count,$addr) = split(/<>/);

			print "<tr><th><input type=radio name=no value=\"$no\"></th>";
			print "<th>$item</th><td><b>0</b></td><th>$count</th>";
			print "<td><img src=\"$graph\" height=15 width=1> 0.0\%</td></tr>\n";
		}
		close(IN);
	}
	# ソート処理
	else {
	    # ランク用中間ファイル
	    $rank1 = 0;
	    $rank2 = 1;
	    $count_tmp = 0;

	    # ソート処理
	    foreach (sort { ($item{$b} <=> $item{$a}) || ($a cmp $b) } keys(%count)) {
		($count{$_} == $count_tmp) || ($rank1 = $rank2);

		$per = int(($count{$_}*1000 / $total)+0.5) / 10;
		$per = sprintf("%.1f", $per);
		$width = int($per * 5);
		if ($width < 1) { $width = 1; }

		# 桁区切り
		$cnt = &filler($count{$_});

		print "<tr><th><input type=radio name=no value=\"$_\"></th>";
		print "<td><b>$item{$_}</b></td><td align=right><b>$cnt</b></td>";
		print "<td><img src=\"$graph\" height=15 width=\"$width\"> $per\%</td></tr>\n";

#		$count_tmp = $count{$_};
#		$rank2++;
	    }
	}
	print "</table>\n<P><input type=submit value=\"回答する\">";
	print "<input type=reset value=\"リセット\"></form>\n";

	# 項目追加
#	if ($freeitem) {
#		print "<hr width=400 size=1>\n";
#		print "<form action=\"$script\" method=\"POST\">\n";
#		print "<input type=hidden name=mode value=\"item\">\n";
#		print "投票する項目が上記にない場合は以下のフォームから追加して下さい<br><br>\n";
#		print "<input type=text name=item size=25>\n";
#		print "<input type=submit value='項目を追加'></form>\n";
#		print "<hr width=400 size=1>\n";
#	}

	# 管理用フォーム
	print "<div align=right>\n";
	print "<form action=\"$script\" method=\"POST\">\n";
	print "<input type=hidden name=mode value=\"admin\">\n";
	print "<input type=password name=pass size=8>";
	print "<input type=submit value='管理用'></form></div>\n";

	# 著作権表示（削除不可）
print "<div align=right>\n";
	print "$banner2<P><small><!-- $ver -->\n";
	print "- <a href='http://www.kent-web.com/' target='_blank'>WebEnquete</a> -\n";
	print "</small></div>\n</body>\n</html>\n";
	exit;
}

#----------------#
#  投票受付処理  #
#----------------#
sub regist {
	local($ref, $addr, $top, $no, $item, $count, $host, @ans, $ans, @new);

	# セキュリティチェック
	if ($postonly && !$post_flag) {
		&error("不正な処理のため処理を中断しました");
	}
	if ($myurl) {
		$ref = $ENV{'HTTP_REFERER'};
		$ref =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg;
		if ($ref !~ /$myurl/i) { &error("不正なアクセスです"); }
	}

	# IPアドレスを取得
	$addr = $ENV{'REMOTE_ADDR'};

	# 投票情報
	if ($in{'no'} eq "") { &error("投票情報がありません"); }
	else { @ans = split(/\0/, $in{'no'}); }

	# ロック開始
	&lock if ($lockkey);

	@new=();
	open(IN,"$logfile") || &error("Open Error : $logfile");
	$top = <IN>;
	chop($top);
	if ($ip_check && $addr eq $top) {
		close(IN);
		&error("連続投票はできません");
	}
	while (<IN>) {
		($no,$item,$count,$host) = split(/<>/);

		foreach $ans (@ans) {
			if ($no == $ans) {
				$count++;
				$_ = "$no<>$item<>$count<>$host<>\n";
				last;
			}
		}
		push(@new,$_);
	}
	close(IN);

	# 更新
	unshift(@new,"$addr\n");
	open(OUT,">$logfile") || &error("Write Error : $logfile");
	print OUT @new;
	close(OUT);

	# ロック解除
	&unlock if ($lockkey);

	# 完了メッセージ
	&header;
	print <<"EOM";
<center>
<P><hr width="75%">
<h3>ご回答ありがとうございました</h3>
<form action="$script" method="GET">
<input type=submit value="一覧にもどる"></form>
<P><hr width="75%">
</center>
</body>
</html>
EOM
	exit;
}

#--------------#
#  管理モード  #
#--------------#
sub admin {
	local($top, $item, $flag, $count, $host, @lines, @new);

	# パスワードチェック
	if ($in{'pass'} ne $pass) { &error("パスワードが違います"); }

	# 項目追加処理
	if ($in{'item'} && !$in{'ope'}) {

		# ロック開始
		&lock if ($lockkey);

		# ログ読込
		open(IN,"$logfile") || &error("Open Error : $logfile");
		@lines = <IN>;
		close(IN);
		$top = shift(@lines);

		# 項目Noを採番
		($no) = split(/<>/, $lines[0]);
		$no++;

		# 更新
		unshift(@lines,"$no<>$in{'item'}<>0<><>\n");
		unshift(@lines,$top);
		open(OUT,">$logfile") || &error("Write Error : $logfile");
		print OUT @lines;
		close(OUT);

		# ロック解除
		&unlock if ($lockkey);
	}
	# 修正フォーム
	elsif ($in{'ope'} eq 'mente' && $in{'no'}) {

		open(IN,"$logfile") || &error("Open Error : $logfile");
		$top = <IN>;
		@new = ($top);
		while (<IN>) {
			($no,$item,$count,$host) = split(/<>/);
			last if ($in{'no'} == $no);
		}
		close(IN);

		&header;
		print "<h3>項目修正フォーム</h3>\n";
		print "- 修正する項目のみ変更して送信ボタンを押して下さい<br>\n";
		print "<form action=\"$script\" method=POST>\n";
		print "<input type=hidden name=pass value=\"$in{'pass'}\">\n";
		print "<input type=hidden name=mode value=admin>\n";
		print "<input type=hidden name=ope value=mente2>\n";
		print "<input type=hidden name=no value=\"$in{'no'}\">\n";
		print "項目名 <input type=text name=item size=25 value=\"$item\"><br>\n";
		print "投票数 <input type=text name=count size=8 value=\"$count\"><br>\n";
		print "<input type=submit value='送信する'></form>\n";
		print "</body>\n</html>\n";
		exit;
	}
	# 修正実行
	elsif ($in{'ope'} eq 'mente2' && $in{'no'}) {

		# 入力チェック
		if ($in{'item'} eq '') { &error('項目名に入力がありません'); }
		if ($in{'count'} =~ /\D/) { &error('投票数に半角数字以外の文字が入力されています'); }

		# ロック開始
		&lock if ($lockkey);

		open(IN,"$logfile") || &error("Open Error : $logfile");
		$top = <IN>;
		@new = ($top);
		while (<IN>) {
			($no,$item,$count,$host) = split(/<>/);
			if ($in{'no'} == $no) {
				$_ = "$no<>$in{'item'}<>$in{'count'}<>$host<>\n";
			}
			push(@new,$_);
		}
		close(IN);

		# 更新
		open(OUT,">$logfile") || &error("Write Error : $logfile");
		print OUT @new;
		close(OUT);

		# ロック解除
		&unlock if ($lockkey);
	}
	# 削除処理
	elsif ($in{'ope'} eq 'del' && $in{'no'}) {

		# ロック開始
		&lock if ($lockkey);

		open(IN,"$logfile") || &error("Open Error : $logfile");
		$top = <IN>;
		@new = ($top);
		while (<IN>) {
			($no) = split(/<>/);
			next if ($in{'no'} == $no);
			push(@new,$_);
		}
		close(IN);

		# 更新
		open(OUT,">$logfile") || &error("Write Error : $logfile");
		print OUT @new;
		close(OUT);

		# ロック解除
		&unlock if ($lockkey);
	}

	# 管理画面
	&header;
	print <<"EOM";
[<a href="$script?">初期画面に戻る</a>]
<table width="100%"><tr><th bgcolor="#880000">
<font color="#FFFFFF">管理モード</font></table>
<h3>1. 項目の追加</h3>
<form action="$script" method="POST">
<input type=hidden name=pass value="$in{'pass'}">
<input type=hidden name=mode value="admin">
項目 <input type=text name=item size=30>
<input type=submit value="項目を追加"></form>
<hr><h3>2. データのメンテ</h3>
<form action="$script" method="POST">
<input type=hidden name=pass value="$in{'pass'}">
<input type=hidden name=mode value="admin">
<input type=submit value="選択する">
<select name=ope>
<option value="mente">修正
<option value="del">削除</select>
<P><table border=1 cellspacing=0>
<tr><th>選択<th>項目名<th>得票数
EOM
	if ($freeitem) { print "<th>項目作成者<br>ホスト情報\n"; }

	open(IN,"$logfile") || &error("Open Error : $logfile");
	$top = <IN>;
	while (<IN>) {
		($no,$item,$count,$host) = split(/<>/);
		print "<tr><th><input type=radio name=no value=\"$no\">";
		print "<td><b>$item</b><th>$count\n";
		if ($freeitem) {
			if (!$host) { $host = "<br>"; }
			print "<td>$host\n";
		}
	}
	close(IN);

	print "</table></form>\n</body>\n</html>\n";
	exit;
}

#------------------#
#  ユーザ項目追加  #
#------------------#
sub make_item {
	local($flag, $no, $item, $count, $dom, $host);

	# フォームチェック
	if ($in{'item'} eq "") { &error("項目名に入力がありません"); }

	# ホスト名チェック
	($addr, $host) = &get_host;

	# ロック開始
	&lock if ($lockkey);

	# ログ読込
	open(IN,"$logfile") || &error("Open Error : $logfile");
	@lines = <IN>;
	close(IN);
	shift(@lines);

	# 重複項目のチェック
	$flag=0;
	foreach (@lines) {
		($no,$item) = split(/<>/);
		if ($in{'item'} eq $item) { $flag=1; last; }
	}
	if ($flag) { &error("ご指定の項目は既に登録されています"); }

	($no,$item,$count,$dom) = split(/<>/, $lines[0]);
	# 連続登録チェック
	if ($freeitem == 1 && $host eq $dom) {
		&error("連続した項目追加はできません");
	}
	# 項目Noを採番
	$no++;

	# 更新
	unshift(@lines,"$no<>$in{'item'}<>1<>$host<>\n");
	unshift(@lines,"$addr\n");
	open(OUT,">$logfile") || &error("Write Error : $logfile");
	print OUT @lines;
	close(OUT);

	# ロック解除
	&unlock if ($lockkey);
}

#----------------#
#  デコード処理  #
#----------------#
sub decode {
	local($buffer, $name, $value, @pairs);

	if ($ENV{'REQUEST_METHOD'} eq "POST") {
		read(STDIN, $buffer, $ENV{'CONTENT_LENGTH'});
		$post_flag=1;
	} else {
		$buffer = $ENV{'QUERY_STRING'};
		$post_flag=0;
	}
	@pairs = split(/&/, $buffer);
	foreach (@pairs) {
		($name,$value) = split(/=/);
		$value =~ tr/+/ /;
		$value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg;

		&jcode'convert(*value,'sjis');

		$value =~ s/&/&amp;/g;
		$value =~ s/"/&quot;/g;
		$value =~ s/</&lt;/g;
		$value =~ s/>/&gt;/g;

		$in{$name} .= "\0" if (defined($in{$name}));
		$in{$name} .= $value;
	}
	$mode = $in{'mode'};
}

#--------------#
#  HTMLヘッダ  #
#--------------#
sub header {
	$head_flag=1;
	print "Content-type: text/html\n\n";
	print <<"EOM";
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html lang="ja">
<head>
<META HTTP-EQUIV="Content-type" CONTENT="text/html; charset=Shift_JIS">
<META HTTP-EQUIV="Content-Style-Type" content="text/css">
<META HTTP-EQUIV="Pragma" CONTENT="no-cache">
<STYLE type="text/css">
<!--
body,tr,td,th { font-size:$b_size; font-family:"$f_family"; }
small { font-size:9pt; }
-->
</STYLE>
<title>$title</title></head>
$body
EOM
}

#--------------#
#  エラー処理  #
#--------------#
sub error {
	if ($lockflag) { rmdir($lockfile); }

	&header if (!$head_flag);
	print "<center><hr width='400'><h3>ERROR !</h3>\n";
	print "<font color='#CC0000'>$_[0]</font>\n";
	print "<P><hr width='400'></center>\n</body>\n</html>\n";
	exit;
}

#--------------#
#  ロック処理  #
#--------------#
sub lock {
	local($retry, $mtime);

	# 1分以上古いロックは削除する
	if (-e $lockfile) {
		($mtime) = (stat($lockfile))[9];
		if ($mtime < time - 60) { &unlock; }
	}
	# ロック処理
	$retry=5;
	while (!mkdir($lockfile, 0755)) {
		if (--$retry <= 0) { &error('LOCK is BUSY'); }
		sleep(1);
	}
	$lockflag=1;
}

#--------------#
#  ロック解除  #
#--------------#
sub unlock {
	rmdir($lockfile);
	$lockflag=0;
}

#------------#
#  桁区切り  #
#------------#
sub filler {
	local($_) = $_[0];
	1 while s/(.*\d)(\d\d\d)/$1,$2/;
	return $_;
}

#----------------#
#  ホスト名取得  #
#----------------#
sub get_host {
	local($host) = $ENV{'REMOTE_HOST'};
	local($addr) = $ENV{'REMOTE_ADDR'};

	if ($host eq "" || $host eq $addr) {
		$host = gethostbyaddr(pack("C4", split(/\./, $addr)), 2) || $addr;
	}
	return ($addr, $host);
}

#------------------#
#  チェックモード  #
#------------------#
sub check {
	&header;
	print "<h2>Check Mode</h2>\n";
	print "<UL>\n";

	# ログパス
	if (-e $logfile) {
		print "<LI>ログファイルのパス：OK\n";
		# ログパーミッション
		if (-r $logfile && -w $logfile) {
			print "<LI>ログファイルのパーミッション：OK\n";
		} else { print "<LI>ログファイルのパーミッション：NG\n"; }
	} else { print "<LI>ログファイルのパス：NG → $logfile\n"; }

	# ロックディレクトリ
	print "<LI>ロック形式：";
	if ($lockkey == 0) { print "ロック設定なし\n"; }
	else {
		print "ロック設定あり\n";

		($lockdir) = $lockfile =~ /(.*)[\\\/].*$/;
		print "<LI>ロックディレクトリ：$lockdir\n";

		if (-d $lockdir) { print "<LI>ロックディレクトリのパス：OK\n"; }
		else { print "<LI>ロックディレクトリのパス：NG → $lockdir\n"; }

		if (-r $lockdir && -w $lockdir && -x $lockdir) {
			print "<LI>ロックディレクトリのパーミッション：OK\n";
		} else {
			print "<LI>ロックディレクトリのパーミッション：NG → $lockdir\n";
		}
	}

	print "</UL>\n</body>\n</html>\n";
	exit;
}

__END__

