#! /usr/bin/perl
#
# attlist4.cgi
#
# 6.017 : 8/1/08 : ID機能により複数設置可に変更
# 6.016 : 4/24/08 : IEで全角スペースのみのコメントリンクが表示されないのを修正
# 6.015 : 4/23/08 : コメントの半角スペースの処理を修正
# 6.014 : 10/14/07 : 管理人オンリーモードを追加
# 6.013 : 10/10/07 : ヘッダを繰り返し表示するオプションを追加
# 6.012 : 9/28/07 : 更新時刻表示に曜日表示のオプションを追加
# 6.011 : 8/2/07 : 携帯用の「ページ上部、下部」を追加
# 6.010 : 5/5/07 : 管理人一括更新での日付の更新を修正
# 6.009 : 4/23/07 : 管理人一括更新機能を追加。スパム投稿防止機能を追加。
# 6.008 : 2/3/07 : テーブル表示幅とコメント欄表示幅オプションを追加
# 6.007 : 1/30/07 : 参加者ファイルを使用しないように修正。
# 6.006 : 1/24/07 : コメントの半角カンマのバグを修正
# 6.005 : 1/12/07 : ファイルロックを修正
# 6.004 : 11/5/06 : 携帯モードからのコメント修正のバグを修正
# 6.003 : 10/28/06 : 定員達成時に出席以外は変更できるように修正
# 6.002 : 10/26/06 : 定員達成時に出席以外の参加人数受付オプションを追加
# 6.001 : 10/22/06 : 携帯モードを追加
# 6.0 : 10/21/06 : attlist3にコメント機能を追加
#
# http://www.hidekik.com
#
# $Id: attlist4.cgi,v 1.22 2008/08/01 03:54:25 Hideki Kanayama Exp $
# Copyright(c) 1997-2008, Hideki Kanayama, All rights reserved.
use CGI qw(:cgi-lib);
use strict;
use CGI::Carp qw(fatalsToBrowser);
use File::Basename;
use Time::Local;
my $version = "6.017";
my $lastupdatedyear = "2008";
my $admindat = "adminpwd.dat";
my $setupfile = "attlist4_setup.pl";
my $script = basename($0);
my $lang = 0;
my $charset = ("Shift_JIS","ISO-8859-1")[$lang];
########## 環境設定 ここから ###########################
our $datafile = "attend.dat";
# lockfile
our $lockfile = "lockfile.dat";
# Title
our $title = '出欠表 ';
# Background
our $bgimage_en = 0;
our $bgimage_file = '';
our $bgcolor = "#ffffff";
# Back link
our $backlink_en = 1;
our $backlink = '..';
our $backlink_name = '戻る';
# Body width
our $body_width = '100';
# option cell back ground color
our $option_bg_color = "#ffcc33";
# option cell font color
our $option_font_color = "black";
# item cell back ground color
our $item_bg_color = "#66ff99";
# item cell font color
our $item_font_color = "black";
#表示テーブル幅 表示幅からの%
our $table_width = 50;
#表示テーブルのカラム数
our $table_colms = 1;
#参加者設定の初期登録可能人数
our $default_max = 15;
#管理用リンク表示 1:有効 0:無効
our $setup_en = 1;
#変更日表示 1:有効 0:無効
our $update_en = 1;
our $update_type = 0;
our $weekday_en = '';
#コメント機能 1:有効 0:無効
our $comment_en = 1;
our $comment_box = 20;
our $comment_width = 30;
#削除チェック 1:有効 0:無効
our $delcheck_en = 1;
#携帯モード編集 1:有効 0:無効
our $medit_en = 1;
#定員設定 1:有効 0:無効
our $att_limit_en = 0;
our $att_limit_num = 10;
#定員に達した後出席以外の登録を有効にする 1:有効 0:無効
our $allow_absent_beyond_limit = 0;
#締め切り 1:有効 0:無効
our $deadline_en = 0;
our $deadline_year = 2006;
our $deadline_month = 3;
our $deadline_day = 25;
our $deadline_hour = 0;
our $deadline_min = 0;
#パスワード保護 1:有効 0:無効
our $password_en = 1;
#管理人オンリーモード 1:有効 0:無効
our $adminonly_en = 0;
our $newreg_adminpwd = 0;
#カウント 1:有効 0:無効
our $count_en = 0;
our $count_name = '参加人数';
#管理人一括更新ボタン 1:有効 0:無効
our $adminbutton_en = 0;
#オプション オプション名,背景色
our $options = '出席,red
欠席,blue
未定,green
';
#ヘッダ表示
our $header_repeat = 20;
# NG word
our $ngword = 'http url';
# body_head
our $body_head = '
出欠表
';
# body_tail
our $body_tail = '
';
# body_head for cell phone
our $body_head_for_cell = '
出欠表
';
# body_tail for cell phone
our $body_tail_for_cell = '
';
# Style Sheet
our $style_sheet_en = 0;
our $style_sheet = '
';
# Head insert
our $head_insert_en = 0;
our $head_insert = '
';
#時間設定 1:localtime, 0:offset from GMT
our $localtime_en = 1;
our $offset_from_gmt = 9;
#予備のアップデートファイル
# 1: on, 0: off
our $update1_file_en = 0;
our $update2_file_en = 0;
our $update1_file = "update1.log";
our $update2_file = "update2.log";
########## 環境設定 ここまで ###########################
require "$setupfile" if (-e "$setupfile");
my $q = CGI->new;
my $cgierror = $q->cgi_error;
&error($cgierror) if ($cgierror);
my %in = &postprocess($q->Vars);
if ($script eq 'atmobile.cgi'){
$script = 'attlist4.cgi';
$in{mode} = 'mobile';
}
if ($in{id} ne ''){
$datafile = "$in{id}-$datafile";
}
if (! -e "$admindat"){
if ($in{mode} ne 'wradminpwd'){
&setadminpwd;
} else {
&wradminpwd($in{pwd});
}
}
my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst)=
$localtime_en ? localtime(time) : gmtime(time + 3600 * $offset_from_gmt);
my $update = sprintf("%s_%s_%s_%02s_%02s",$year+1900,$mon+1,$mday,$hour,$min);
my $deadline_time = timelocal(0,$deadline_min,$deadline_hour,$deadline_day,$deadline_month-1,$deadline_year);
my $now = timelocal(localtime);
my @items = &get_items;
my ($maxnum, $date, $lastname, $name, $status, $count, $eachpwd, $eachmod, $comment) = &get_data;
my (@name, @status, @count, @eachpwd, @eachmod, @comment);
@name = @{$name};
@status = @{$status};
@count = @{$count};
@eachpwd = @{$eachpwd};
@eachmod = @{$eachmod};
@comment = @{$comment};
my @valid_list = grep /^0$/, @status;
my $attnum = $#valid_list + 1;
my $hit_limit = 0;
my $update_button = 1;
my @option_list = grep !/^\s*$/, split /[\r\n]/, $options;
my @newoption_list;
foreach (@option_list){
chomp;
my ($a,$b) = split /,/;
push(@newoption_list,$a);
}
my @ngword = split /\s/, $ngword;
if ($in{mode} eq 'atwrite'){
&atwrite;
} elsif ($in{mode} eq 'setup'){
&setup;
} elsif ($in{mode} eq 'setup_update'){
&setup_update;
} elsif ($in{mode} eq 'mobile'){
&mobile;
} elsif ($in{mode} eq 'medit'){
&medit;
} else {
&display;
}
###################### 書き込み ############################
sub atwrite {
if ($adminonly_en and $in{add} ne '' and !&checkadmin($in{newpwd})){
&error('管理人オンリーモードなので管理人のみ追加できます。');
}
unless (&check_adminbutton or $in{adminbutton} eq ''){
&error('管理用パスワードが違います。');
}
if ($deadline_en and $now > $deadline_time){
&error('締め切り日時を過ぎているので追加、変更できません。');
}
if ($in{add} ne ''){
&newregister;
} else {
&update;
}
&extraupdate;
print "Location: $script";
print "?id=$in{id}" if ($in{id} ne '');
if ($in{'next'} eq 'mobile') {
print $in{id} ? '&' : '?';
print "mode=mobile";
}
print "\n\n";
}
sub newregister {
&error('名前は必ず入れてください')if ($in{newname} =~ /^\s*$/);
&error('パスワードは必ず入れてください')if ($in{newpwd} eq "" and $password_en);
my $invalid = 0;
foreach (@ngword){
foreach my $in ($in{newname},$in{newcomment}){
$invalid = 1 if ($in =~ /$_/i);
}
}
if ($invalid) {
&error('不適当な言葉が含まれています。');
}
if (($att_limit_en and $attnum >= $att_limit_num and
!($allow_absent_beyond_limit and $in{newoption} != 0))){
&error('締め切り人数に達しているので追加できません。。');
}
&lockfile;
my ($maxnum, @dummy) = &get_data;
open (TMPFILE, "> tmp.$$") or &error("tempfileを作成できません。");
print TMPFILE "0,$in{newname},$update\n";
if (open(DAT,"< $datafile")) {
flock DAT, 1;
my $i = 0;
while (){
print TMPFILE "$_" if ($i != 0);
$i++;
}
close(DAT);
}
$maxnum++;
$in{newoption} = 0 if ($in{newoption} eq '');
my $encpwd = ($password_en and ($adminonly_en == 0 or ($adminonly_en and $newreg_adminpwd))) ? &makecrypt($in{newpwd}) : '';
print TMPFILE "$maxnum,$in{newname},$in{newoption},$in{newcount},$update,$encpwd,$in{newcomment}\n";
close(TMPFILE);
rename "tmp.$$", $datafile;
&unlockfile;
}
sub update {
&lockfile;
my $tmpfile = "tmp.$$";
open(WRDAT,">$tmpfile") or &error("$tmpfileが開けません。");
my $endatt = $#items + 1;
my $i;
for ($i=1;$i<=$endatt;$i=$i+1){
my $btni = "btn_$i";
if ($in{$btni} ne ''){
$lastname = $name[$i];
last;
}
}
if ($in{adminbutton} ne ''){
$lastname = '管理人';
}
print WRDAT "0,$lastname,$update,\n";
my $newstatus;
my $newcount;
my $newcomment;
my $modified = 0;
for ($i=1;$i<=$endatt;$i=$i+1){
my $pwdi = "pwd_$i";
my $btni = "btn_$i";
$newstatus = $status[$i];
$newcount = $count[$i];
$newcomment = $comment[$i];
chomp($items[$i-1]);
if (($in{$btni} ne '' and
($password_en == 1 &&
&checkcrypt("$in{$pwdi}","$eachpwd[$i]"))) or
(&check_adminbutton and $in{adminbutton} ne '')) {
$modified = 1;
}
if ((($in{$i} ne $status[$i] or
($in{"count_$i"} != $count[$i] and $count_en) or
($in{"comment_$i"} ne $comment[$i] and $comment_en))
and $in{$btni} ne '') or
((&check_adminbutton and $in{adminbutton} ne '') and
($in{$i} ne $status[$i] or $in{delallcom} eq 'on'))
) {
$eachmod[$i] = "$update";
$modified = 1;
if (($password_en == 1 && "$eachpwd[$i]" ne '' &&
!&checkcrypt("$in{$pwdi}","$eachpwd[$i]")) and
!&check_adminbutton) {
close(WRDAT);
unlink("$tmpfile");
&error("$name[$i]を変更しようとしましたが、そのパスワードが違います。");
}
$newstatus = $in{$i} unless ($newstatus eq 'deleted');
$newcount = $in{"count_$i"};
$newcomment = $in{"comment_$i"} if ($in{edit} == $i);
$newcomment = '' if ($in{delallcom} eq 'on' and $in{adminbutton} ne '');
my $invalid = 0;
foreach (@ngword){
$invalid = 1 if ($newcomment =~ /$_/i);
}
if ($invalid){
close(WRDAT);
unlink("$tmpfile");
&error('不適当な言葉が含まれています。');
}
if ($att_limit_en and $attnum >= $att_limit_num and $newstatus eq '0'){
close(WRDAT);
unlink("$tmpfile");
&error('締め切り人数に達しているので追加、変更できません。');
}
}
if ($password_en == 1 && ("$in{$pwdi}" ne '' && "$eachpwd[$i]" eq '')){
$eachpwd[$i] = &makecrypt("$in{$pwdi}");
}
print WRDAT "$i,$items[$i-1],$newstatus,$newcount,$eachmod[$i],$eachpwd[$i],$newcomment\n";
}
close(WRDAT);
if ($modified){
rename("$tmpfile","$datafile");
} else {
unlink "$tmpfile";
}
chmod(0666,"$datafile");
&unlockfile;
}
sub check_adminbutton {
return ($adminbutton_en and &checkadmin($in{adminpwd}));
}
###################### 表示 ############################
sub display {
&htmlhead($title);
if ($att_limit_en and $attnum >= $att_limit_num){
$hit_limit = 1;
}
if ($deadline_en and $now >= $deadline_time){
$delcheck_en = 0;
$password_en = 0;
$update_button = 0;
}
print "$body_head";
print "";
print "$body_tail";
&htmltail;
}
###################### セットアップ ############################
sub setup {
&setadminpwd if ($in{pwd} eq '');
&error('管理用パスワードが違います。') unless &checkadmin($in{pwd});
&htmlhead('管理用セットアップ');
print "\n";
print "";
©right;
print "
";
&htmltail;
}
###################### セットアップ作成 ############################
sub wrsetup {
&error('パスワードが違います。') unless (&checkadmin($in{pwd}));
open(SETUP,"> $setupfile") || &error('セットアップファイルが作成できません。
CGIを置いてあるディレクトリが書き込み可能\か確認してください。');
foreach (keys(%in)){
$in{$_} =~ s/
/\n/ig if (!/body_head/ and !/body_tail/ and !/count_name/ and !/options/);
$in{$_} =~ s/,/,/g;
$in{$_} =~ s/<//g;
$in{$_} =~ s/\s*$//;
}
print SETUP <header(-charset=>$charset);
print "\n";
print "\n";
print "\n";
print "$title\n";
if ($head_insert_en == 1){
print "$head_insert\n";
}
if ($style_sheet_en == 1){
print "\n";
}
print "\n";
print "\n";
print "| \n";
}
sub copyright {
my $mysite = ('http://www.hidekik.com/','http://www.hidekik.com/en/')[$lang];
print "$script Ver. $version\n";
print "Copyright(C) 1997-$lastupdatedyear, hidekik.com\n";
}
sub htmltail {
print " |
\n";
exit;
}
sub error {
my ($msg) = shift;
&unlockfile;
&htmlhead($msg);
print "
$msg\n";
print "
";
©right;
print "
";
&htmltail;
exit;
}
sub makecrypt {
my $plain = shift;
my $salt = join "", ('.', '/', 0..9, 'A'..'Z', 'a'..'z')[rand 64, rand 64];
my $result = crypt($plain,$salt) or crypt($plain,'$1$'.$salt.'$');
return $result;
}
sub setadminpwd {
my $subname = 'setadminpwd';
&htmlhead('管理者用パスワードを入力してください');
print "