#!/usr/local/bin/perl
#
#############################################################
###
### CGI多数投票 Ver.1.0
### [1/2] 本体
###
### (C) 2001 Sentora Dori
### http://www.lowpower.iis.u-tokyo.ac.jp/~hat
###
#############################################################
#
#################### 変数設定 ####################
# アンケートデータをおくディレクトリのパス
$databasedir = ".";
# HTMLファイルをおくディレクトリのパス
$htmlbasedir = ".";
# 入力フォームを挿入する場所を示すタグのベース
$baseinputtag = 'INPUT';
# アンケート結果を挿入する場所を示すタグのベース
$baseresulttag = 'RESULT';
# 新しく入力される項目の最大の長さ
$maxlength = 80;
# テーブルの枠線の太さ
$tableborder = 1;
# テーブルの背景色
$tablecolor = '';
# 1: ログをとる 0: ログをとらない
$takelog = 1;
# post: postメソッド get: getメソッド
$method = 'post';
# 2: flockを使用する 1: ロックファイルでロックする 0: ロックしない (通常は変更必要なし)
$uselock = 2;
# 漢字コード(sjis or jis or euc)
$kanjicode = 'sjis';
#################### 変数設定(ここまで) ####################
# 漢字ライブラリのファイル名
$jcodelib ='./jcode.pl';
# 次のアンケートデータとの区切り記号 (変更必要なし)
$delimiter = "---nextenq---\n";
# flockを使用しないロックのときのロックファイル名のベース (変更必要なし)
$baselockfile = './SDlock';
# このファイル名
$thisurl = 'multivote.cgi';
#----- 漢字コード設定 -----
require "$jcodelib";
if ($kanjicode eq 'sjis') {
$contenttype = "";
# $contenttype = "";
} elsif ($kanjicode eq 'euc') {
$contenttype = "";
# $contenttype = "";
} elsif ($kanjicode eq 'jis') {
$contenttype = "";
}
##### format #####
#----- ユーザからの情報取得 -----
&init_form($kanjicode);
$hostaddr = $ENV{'REMOTE_ADDR'};
$hostname = $ENV{'REMOTE_HOST'};
if ($hostaddr eq $hostname || $hostname eq '') {
$hostname = gethostbyaddr(pack("C4", split(/\./, $hostaddr)), 2);
}
#----- 変数入力 -----
$enqname = $form{'enqname'} if ($form{'enqname'});
$datafile = "$databasedir/$enqname.txt";
$logfile = "$databasedir/$enqname.log";
$htmlname = $form{'htmlname'} if ($form{'htmlname'});
$htmlfile = "$databasedir/$htmlname";
$command = $form{'command'} if ($form{'command'});
$enqtitle = $form{'enqtitle'} if ($form{'enqtitle'});
$enqid = $form{'enqid'} if ($form{'enqid'});
$itemid = $form{'itemid'} if ($form{'itemid'});
$newitem = $form{'newitem'} if ($form{'newitem'});
$enqbegin = $form{'enqbegin'} if ($form{'enqbegin'});
$enqend = $form{'enqend'} if ($form{'enqend'});
$newitem = &norm_input($newitem);
##### branch(入力されたコマンドによる分岐) #####
if ($command eq 'read') {
&do_read;
} elsif ($command eq 'add') {
&do_add;
} elsif ($command eq 'vote') {
&do_vote;
} else {
&do_read;
}
exit(0);
######################
##### subroutine #####
######################
###### do(コマンド実行) #####
#----- アンケート結果を読む(設問と結果の表示) -----
sub do_read {
&show_result;
}
#----- 新しい項目を加える(ファイルに書いてページを表示する) -----
sub do_add {
if (length($newitem) > $maxlength) {
&print_error("入力された項目名が長すぎます。");
} elsif ($newitem eq $delimiter) {
&print_error("入力された名前はデータファイルで特殊記号として使用されているため、受け付けられません。");
}
# data file open
&open_file(TXT, "+<$datafile", "No data file");
&lock_file(TXT);
@txt = ;
# get title and txt
$title = shift(@txt);
$txtlist = join('', @txt);
@result = split(/$delimiter/, $txtlist);
@item = split(/\n/, $result[$enqid-1]);
for ($i = 0; $i < @item; $i++) {
($name , $value) = split(/,/, $item[$i]);
if ($newitem eq $name) {
&print_error("あなたの入力した項目はすでにあります。");
}
}
$value = 1;
@bakitem = ($newitem, $value);
$item[@item] = join(',', @bakitem);
$result[$enqid-1] = join("\n", @item)."\n";
$txtlist = $title.join("$delimiter", @result);
seek(TXT, 0, 0);
print TXT $txtlist;
&unlock_file(TXT);
&close_file(TXT);
if ($takelog) {
&take_log($enqid, $newitem, $value);
}
&show_result;
}
#----- 投票する -----
sub do_vote {
# data file open
&open_file(TXT, "+<$datafile", "No data file");
&lock_file(TXT);
@txt = ;
# get title and txt
$title = shift(@txt);
$txtlist = join('', @txt);
@result = split(/$delimiter/, $txtlist);
@item = split(/\n/, $result[$enqid-1]);
($name , $newvalue) = split(/,/, $item[$itemid]);
$newvalue++;
$bakname = $name;
@bakitem = ($name, $newvalue);
$item[$itemid] = join(',', @bakitem);
# swap
for ($i = $itemid; $i > 0; $i--) {
($name , $value) = split(/,/, $item[$i-1]);
if ($newvalue > $value) {
$bak = $item[$i-1];
$item[$i-1] = $item[$i];
$item[$i] = $bak;
} else {
last;
}
}
$result[$enqid-1] = join("\n", @item)."\n";
$txtlist = $title.join("$delimiter", @result);
seek(TXT, 0, 0);
print TXT $txtlist;
&unlock_file(TXT);
&close_file(TXT);
if ($takelog) {
&take_log($enqid, $bakname, $newvalue);
}
&show_result;
}
##### show(HTML表示) #####
#----- HTMLファイルと結果を表示する -----
sub show_result {
# data file open
&open_file(TXT, "$datafile", "No data file");
@txt = ;
&close_file(TXT);
# get title and txt
$title = shift(@txt);
chomp($title);
$txtlist = join('',@txt);
@result = split(/$delimiter/, $txtlist);
$enqend = $#result+1 if ($enqend == 0);
print "Content-type: text/html\n\n";
for ($i = $enqbegin; $i < $enqend + 1; $i++) {
# make inputform
$maxlength_zenkaku = int($maxlength / 2);
$print_input[$i] = <<"END_INPUTFORM";
END_INPUTFORM
# make result table
@item = split(/\n/, $result[$i-1]);
$print_result[$i] .= " | 内容 | 票数 | |
";
for ($j = 0; $j < @item; $j++) {
($name , $value) = split(/,/, $item[$j]);
$print_result[$i] .= "| $name | $value | 投票 |
";
}
$print_result[$i] .= "
";
}
# html file open
&open_file(HTML, "$htmlfile", "No html file");
@html = ;
&close_file(HTML);
for ($i = 0; $i < @html; $i++) {
for ($j = $enqbegin; $j < $enqend + 1; $j++) {
$inputtag = "<$baseinputtag$j>";
$resulttag = "<$baseresulttag$j>";
if ($html[$i] =~ /$inputtag/i) {
$html[$i] =~ s/$inputtag/$print_input[$j]/gi;
} elsif ($html[$i] =~ /$resulttag/i) {
$html[$i] =~ s/$resulttag/$print_result[$j]/gi;
}
}
print $html[$i];
}
}
##### ログ処理 #####
sub take_log {
local($id, $name, $value) = @_;
local($sec, $min, $hour, $day, $mon, $year);
local($timelog);
# log file open
&open_file(LOGF, "+<$logfile", "log file error!");
&lock_file(LOGF);
@logf = ;
($sec, $min, $hour, $day, $mon, $year) = localtime(time);
$mon++;
$year += 1900;
if ($hour < 10) {
$hour = "0$hour";
}
if ($min < 10) {
$min = "0$min";
}
if ($sec < 10) {
$sec = "0$sec";
}
$timelog ="$year-$mon-$day $hour:$min:$sec";
@bakline = ($id, $name, $value, $timelog, $hostaddr, $hostname);
$writeline = join(',', @bakline);
$writeline .= "\n";
unshift(@logf, $writeline);
seek(LOGF, 0, 0);
print LOGF @logf;
&unlock_file(LOGF);
&close_file(LOGF);
}
##### 入出力処理 ######
#----- ブラウザへの入力データの取得 -----
sub init_form {
local($query, @dataarray, $data, $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'};
}
@dataarray = split(/&/, $query);
if ($dataarray[0] !~ /=/) {
$enqname = $dataarray[0];
$htmlname = $dataarray[1];
$enqbegin = ($dataarray[2] =~ /^\d+$/) ? $dataarray[2] : 1;
$enqend = ($dataarray[3] =~ /^\d+$/) ? $dataarray[3] : 0;
} else {
foreach $data (@dataarray) {
($property, $value) = split(/=/, $data);
$value =~ tr/+/ /;
$value =~ s/%([A-Fa-f0-9][A-Fa-f0-9])/pack("C", hex($1))/eg;
&jcode'convert(*value, $charcode);
$form{$property} = $value;
}
}
}
#----- 入力データからのタグ、メタ文字の消去 -----
sub norm_input {
local($string) = @_;
$string =~ s/&/&/g;
$string =~ s/"/"/g;
$string =~ s/</g;
$string =~ s/>/>/g;
$string =~ s/,/,/g;
$string =~ s/:/:/g;
$string =~ s/;/;/g;
$string =~ s/\r\n/\n/g;
$string =~ s/\r/\n/g;
$string =~ s/\n\n/
/g;
$string =~ s/\n/
/g;
return $string;
}
##### ファイル処理 #####
#----- ファイルオープン -----
sub open_file {
local(*FILE, $name, $msg) = @_;
if (!open(FILE, $name)) {
&print_error("$msg");
}
seek(FILE, 0, 0);
}
#----- ファイルクローズ -----
sub close_file {
local(*FILE) = @_;
close(FILE);
}
#----- ファイルロック -----
sub lock_file {
local($FILE) = @_;
local($LOCK) = "LOCK$FILE";
local($lockfile) = "$baselockfile$FILE";
local($retry) = 10;
if ($uselock == 2) {
eval(flock($FILE, 2));
if ($@) {
return 0;
} else {
return 1;
}
} elsif ($uselock == 1) {
while (-f $lockfile) {
sleep(0.3);
return 0 if (--$retry <= 0);
}
open($LOCK, ">$lockfile") || return 0;
close($LOCK);
return 1;
} else {
return 1;
}
}
#----- ファイルアンロック -----
sub unlock_file {
local($FILE) = @_;
local($lockfile) = "$baselockfile$FILE";
if ($uselock == 2) {
flock($FILE, 8);
} elsif ($uselock == 1) {
unlink($lockfile);
}
}
##### エラー処理 #####
#----- エラー文を出力し終了 -----
sub print_error {
local($msg) = @_;
print "Content-type: text/html\n\n";
print <<"END_PRINT_ERROR";
$contenttype
$msg
$msg
END_PRINT_ERROR
exit(0);
}