#!/usr/bin/perl

## Message ANQ v1.11 (2000/10/03)
## Copyright(C) Kent Web 2000
## webmaster@kent-web.com
## http://www.kent-web.com/

$ver = 'Msg-Anq v1.11'; # バージョン情報

#---- [注意事項] -----------------------------------------------#
# 1. このスクリプトはフリーソフトです。このスクリプトを使用	#
#    したいかなる損害に対して作者は一切の責任を負いません。	#
# 2. 設置に関する質問はサポートコーナにお願いいたします。	#
#    直接メールによる質問は受け付けておりません。		#
#---------------------------------------------------------------#

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

# jcode.pl取り込み
require './jcode.pl';

# タイトル名
$title = "tetish dog";

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

# タイトル文字のタイプ
$t_face = "ＭＳ Ｐゴシック";

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

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

# スクリプト名
$script = "./mesganq.cgi";

# ログファイル
$logfile = "./mesganq.log";

# メッセージログファイル
$msgfile = "./mesganq.dat";

# メッセージファイル最大保持数
$max = 10;

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

# ユーザ項目追加機能 (0=no 1=yes)
#  → yes だとユーザが自由に項目を追加することが可能となります
$FreeItem = 0;

# IPアドレスの保持件数（二重投稿禁止）
#  → これを 0 にすると二重投稿のチェックを行ないません
$ipmax = 5;

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

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

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

# method形式 (POST or GET)
$method = 'POST';

# ロックファイル機構 (0=no 1=symlink関数 2=open関数)
$lockkey = 0;

# ロックファイル名
#  --> このディレクトリのパーミッションは 777 に設定
$lockfile = "./mesganq.lock";

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

# サブタイトル
#  --> タイトル下にサブタイトルを記述します（タグ使用可）
$subtitle = <<'EOM';
<!-- ここから -->
<OL>
  <LI>あなたの一番好きな犬を選んで投票して下さい。
  <LI>もし、選択項目からに無い場合はコメントして下さいね。
  <LI>こちらで追加致します。また、皆さんのご意見・ご要望もお待ちしております。
</OL>
<!-- ここまで -->
EOM

# タイトル画像を使う場合 (http://から画像を指定)
$ImgT = "";

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

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

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

# メイン処理
&decode;
if ($mode eq 'regist') { &regist; }
elsif ($mode eq 'admin') { &admin; }
elsif ($mode eq 'MakeItem' && $FreeItem) { &MakeItem; }
&html;

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

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

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

	# タイトル
	if ($ImgT) {
		print "<img src=\"$ImgT\" width=$ImgW height=$ImgH alt=\"$title\" alt=\"$title\"><P>\n";
	}
	else {
		print "<hr width=400>\n";
		print "<font color=\"$t_color\" face=\"$t_face\" size=5><span>$title</span></font>\n";
		print "<hr width=400>\n";
	}
	print "<table><tr><td>\n$subtitle\n</td></tr></table>\n";

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

		push(@NO,$no);
		push(@ITEM,$item);

		$no{$no}    = $no;
		$item{$no}  = $item;
		$count{$no} = $count;

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

	$all = &filler($total);
	print "<form action=\"$script\" method=\"$method\">\n";
	print "<input type=hidden name=mode value=\"regist\">\n";
	print "有効回答：<b>$all</b>件<br><br>\n";
	print "<table border=1 cellpadding=2 cellspacing=0>\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>1</th>";
			print "<td><b>$item</b></td><th>$count</th>";
			print "<td><img src=\"$graph\" height=15 width=1> ";
			print "<small>0.0\%</small></td></tr>\n";
		}
		close(IN);
	}
	# ソート処理
	else {
	    # ランク用中間ファイル
	    $rank1 = 0;
	    $rank2 = 1;
	    $count_tmp = 0;

	    # ソート処理
	    foreach (sort { ($count{$b} <=> $count{$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 * 3);
		if ($width < 1) { $width = 1; }

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

		print "<tr><th>$rank1</th>";
		print "<td><b>$item{$_}</b></td><td align=right><b>$cnt</b></td>";
		print "<td nowrap><img src=\"$graph\" height=15 width=\"$width\"> ";
		print "<small>$per\%</small></td></tr>\n";

		$count_tmp = $count{$_};
		$rank2++;
	    }
	}
	print "</table>\n";

	# 回答フォーム
	print "<P><table><tr><td><b>項目選択</b></td><td><select name=item>\n";
	print "<option value=\"\">▼選択してください\n";
	foreach (0 .. $#NO) {
		print "<option value=\"$NO[$_]\">$ITEM[$_]\n";
	}
	print "</select></td></tr>\n";
	print "<tr><td colspan=2><b>コメント</b> (全角30文字以内)<br>\n";
	print "<textarea name=comment cols=38 rows=3 wrap=soft></textarea></td></tr>\n";

	print "<tr><th colspan=2><input type=submit value=\"回答する\"><input type=reset value=\"リセット\"></th></tr></table>\n";
	print "</form>\n";

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

	# コメントを表示
	print "</center>▼ <b>直近のコメント一覧</b><hr>\n";
	open(IN,"$msgfile");
	while (<IN>) {
		($date,$item,$comment) = split(/<>/);

		print "日付 ： $date<br>\n";
		print "投票 ： <b>$item</b><br>\n";
		print "コメント ： $comment<hr>\n";
	}
	close(IN);

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

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

#----------------#
#  投票受付処理  #
#----------------#
sub regist {
	# IPアドレスを取得
	$addr = $ENV{'REMOTE_ADDR'};

	if ($in{'item'} eq "") { &error("項目の選択がありません"); }
	if (length($in{'comment'}) > 60)
		{ &error("コメント文が長すぎます。全角30文字以内で記述して下さい"); }

	# ロック処理
	if ($lockkey == 1) { &lock1; }
	elsif ($lockkey == 2) { &lock2; }

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

	if ($ipmax) {
		$top =~ s/\n//;
		@ip = split(/\s+/, $top);
		$match=0;
		$ip="";
		$i=0;
		foreach (@ip) {
			$i++;
			if ($addr eq "$_") { $match=1; last; }
			if ($i < $ipmax) { $ip .= "$_ "; }
		}
		if ($match) { &error("連続投票はできません","lock"); }

		$ip = "$addr $ip\n";
	} else {
		$ip = "\n";
	}

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

	# コメント文記録
	if ($in{'comment'}) {

		open(IN,"$msgfile");
		@lines = <IN>;
		close(IN);

		# 記事数を調整
		while ($max <= @lines) { pop(@lines); }

		# 時間／ホスト名を取得
		&get_time;
		&get_host;

		unshift(@lines,"$date<>$AnsItem<>$in{'comment'}<>$host<>\n");
		open(OUT,">$msgfile");
		print OUT @lines;
		close(OUT);
	}

	# ロック解除
	if (-e $lockfile) { unlink($lockfile); }

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

#--------------#
#  管理モード  #
#--------------#
sub admin {
	if ($in{'pass'} ne "$pass") { &error("パスワードが違います"); }

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

		# ロック処理
		if ($lockkey == 1) { &lock1; }
		elsif ($lockkey == 2) { &lock2; }

		$flag=0;
		open(IN,"$logfile") || &error("Open Error : $logfile","lock");
		while(<IN>) {
			($no,$item2) = split(/<>/);
			if ($item eq "$item2") { $flag=1; last; }
		}
		close(IN);

		if ($flag) { &error("この項目は既に存在するため追加できません","lock"); }

		$no++;

		open(OUT,">>$logfile") || &error("Write Error : $logfile","lock");
		print OUT "$no<>$item<>0<>\n";
		close(OUT);

		# ロック解除
		if (-e $lockfile) { unlink($lockfile); }

	}
	# 削除処理
	if ($DEL[0]) {

		# ロック処理
		if ($lockkey == 1) { &lock1; }
		elsif ($lockkey == 2) { &lock2; }

		open(IN,"$logfile") || &error("Open Error : $logfile","lock");
		@lines = <IN>;
		close(IN);
		$top = shift(@lines);

		@new=();
		foreach (@lines) {
			$flag=0;
			($no,$item) = split(/<>/);
			foreach $x (@DEL) {
				if ($no eq "$x") { $flag=1; last; }
			}
			if ($flag == 0) { push(@new,$_); }
		}

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

		# ロック解除
		if (-e $lockfile) { unlink($lockfile); }
	}

	&header;
	print <<"EOM";
[<a href="$script\?">初期画面に戻る</a>]
<table width="100%"><tr><th bgcolor="#0000A0">
<font color="#FFFFFF">管理モード</font>
</th></tr></table>
<P>
<h4>1. 項目の追加</h4>
<form action="$script" method="$method">
<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>
<P><hr>
<h4>2. 項目の削除</h4>
<form action="$script" method="$method">
<input type=hidden name=pass value="$in{'pass'}">
<input type=hidden name=mode value="admin">
<table border=1>
<tr>
  <th>削除</th><th>項目</th><th>得票数</th>
</tr>
EOM
	open(IN,"$logfile") || &error("Open Error : $logfile");
	$top = <IN>;
	while (<IN>) {
		($no,$item,$count) = split(/<>/);
		print "<tr><th><input type=checkbox name=del value=\"$no\"></th>";
		print "<td><b>$item</b></td><th>$count</th></tr>\n";
	}
	close(IN);

	print "</table>\n";
	print "<P><input type=submit value=\"削除する\"><input type=reset value=\"リセット\">\n";
	print "</form><hr>\n";

	# ロック開始
	if ($in{'action'} eq "mesg_del" && $lockkey == 1) { &lock1; }
	elsif ($in{'action'} eq "mesg_del" && $lockkey == 2) { &lock2; }

	open(IN,"$msgfile") || &error("Open Error : $msgfile","lock");
	@lines = <IN>;
	close(IN);

	# 削除処理
	if ($in{'action'} eq "mesg_del") {
		if ($DEL[0] eq "") { &error("削除項目がありません","lock"); }

		@new=();
		foreach (@lines) {
			$flag=0;
			($date,$mesg) = split(/<>/);

			foreach $xx (@DEL) {
				if ($xx eq "$date") { $flag=1; last; }
			}
			if ($flag == 0) { push(@new,$_); }
		}
		# 更新
		open(OUT,">$msgfile") || &error("Write Error : $msgfile","lock");
		print OUT @new;
		close(OUT);

		# ロック解除
		if (-e $lockfile) { unlink("$lockfile"); }

		@lines = @new;
	}

	print "<h4>3. コメントの削除</h4>\n";
	print "<form action=\"$script\" method=\"$method\">\n";
	print "<input type=hidden name=mode value=admin>\n";
	print "<input type=hidden name=action value=mesg_del>\n";
	print "<input type=hidden name=pass value=\"$in{'pass'}\">\n";
	print "<DL>\n";

	foreach (@lines) {
		($date,$item,$comment,$host) = split(/<>/);

		if (length($comment) > 30) {
			$comment = substr($comment,0,28);
			$comment .= "..";
		}

		print "<DT> <input type=checkbox name=del value=\"$date\"> ";
		print "<b>$item</b> $comment - $date ($host)\n";
	}

	print "</DL>\n";
	print "<input type=submit value='削除する'><input type=reset value='リセット'></form>\n";
	print "</body></html>\n";
	exit;
}

#------------------#
#  ユーザ項目追加  #
#------------------#
sub MakeItem {
	# ロック処理
	if ($lockkey == 1) { &lock1; }
	elsif ($lockkey == 2) { &lock2; }

	$flag=0;
	open(IN,"$logfile") || &error("Open Error : $logfile","lock");
	while(<IN>) {
		($no,$item2) = split(/<>/);
		if ($item eq "$item2") { $flag=1; last; }
	}
	close(IN);

	if ($flag) { &error("この項目は既に存在するため追加できません","lock"); }

	$no++;

	open(OUT,">>$logfile") || &error("Write Error : $logfile","lock");
	print OUT "$no<>$item<>0<>\n";
	close(OUT);

	# ロック解除
	if (-e $lockfile) { unlink($lockfile); }
}

#----------------#
#  デコード処理  #
#----------------#
sub decode {
	if ($ENV{'REQUEST_METHOD'} eq "POST") {
		read(STDIN, $buffer, $ENV{'CONTENT_LENGTH'});
	} else { $buffer = $ENV{'QUERY_STRING'}; }

	@pairs = split(/&/, $buffer);
	foreach $pair (@pairs) {
		($name,$value) = split(/=/, $pair);
		$value =~ tr/+/ /;
		$value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg;

		# シフトJIS変換
		&jcode'convert(*value, "sjis", "", "z");

		# 改行処理
		$value =~ s/\r//g;
		$value =~ s/\n//g;

		# 削除情報
		if ($name eq 'del') { push(@DEL,$value); }

		$in{$name} = $value;
	}
	$mode = $in{'mode'};
	$item = $in{'item'};
}

#--------------#
#  HTMLヘッダ  #
#--------------#
sub header {
	$HEAD = 1; # ヘッダ表示フラグ

	print "Content-type: text/html\n\n";
	print <<"EOM";
<html>
<head>
<META HTTP-EQUIV="Content-type" CONTENT="text/html; charset=Shift_JIS">
<META HTTP-EQUIV="Pragma" CONTENT="no-cache">
<STYLE type="text/css">
<!--
body,tr,td,th { font-size:$b_size }
span  { font-size:$t_size }
small { font-size:9pt }
-->
</STYLE>
<title>$title</title></head>
$body
EOM
}

#--------------#
#  エラー処理  #
#--------------#
sub error {
	if ($_[1] eq "lock" && -e $lockfile) { unlink($lockfile); }

	if ($HEAD eq "") { &header; }
	print "<center><hr width='75%'><P><h3>ERROR !</h3>\n";
	print "<P><font color=red><B>$_[0]</B></font>\n";
	print "<P><hr width='75%'></center>\n";
	print "</body></html>\n";
	exit;
}

#--------------------------------#
#  ロックファイル : symlink関数  #
#--------------------------------#
sub lock1 {
	local($retry) = 5;
	while (!symlink(".", $lockfile)) {
		if (--$retry <= 0) { &error("LOCK is BUSY","lock"); }
		sleep(1);
	}
}

#-----------------------------#
#  ロックファイル : open関数  #
#-----------------------------#
sub lock2 {
	local($flag) = 0;
	foreach (1 .. 5) {
		if (-e $lockfile) { sleep(1); }
		else {
			open(LOCK,">$lockfile")
				|| &error("Write Error : $lockfile","lock");
			close(LOCK);
			$flag = 1;
			last;
		}
	}
	if (!$flag) { &error("LOCK is BUSY","lock"); }
}

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

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

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

#--------------#
#  時間の取得  #
#--------------#
sub get_time {
	# 日時の取得
	$ENV{'TZ'} = "JST-9";
	($sec,$min,$hour,$mday,$mon,$year,$wday) = localtime(time);

	# 日時のフォーマット
	@week = ('Sun','Mon','Tue','Wed','Thu','Fri','Sat');
	$date = sprintf("%04d/%02d/%02d(%s) %02d:%02d:%02d",
			$year+1900,$mon+1,$mday,$week[$wday],$hour,$min,$sec);
}
