#! /usr/bin/perl
#
# 履歴記録CGI
#
# 2.008 : 6/19/08 : 変更、一括削除の機能を追加
# 2.007 : 2/2/08 : 年表示、年月表示の時のマス内表示順を日付順に修正
# 2.006 : 8/14/07 : タグ処理を修正
# 2.005 : 7/29/07 : 日付チェック方法を修正
# 2.004 : 6/10/07 : うるう年を修正
# 2.003 : 5/18/07 : 書き込みページを別ページにするオプションを追加
# 2.002 : 5/10/07 : 0から始まる日付を修正。
# 2.001 : 5/9/07 : 0から始まる年を修正。時刻を修正。
# 2.0 : 5/6/07 : 「ヒストリー」より西暦1年から指定できるようにし、
# 2038年問題を修正。カラム数を変更できるように修正。
#
# http://www.hidekik.com/
#
# $Id: history2.cgi,v 1.14 2008/06/19 06:17:09 Hideki Kanayama Exp $
# Copyright(c) 2005-2008, Hideki Kanayama, All rights reserved.
use strict;
use CGI qw(:cgi-lib);
use CGI::Carp qw(fatalsToBrowser);
use File::Basename;
use Time::Local;
use Date::Calc qw(Delta_Days check_date);
my $adminpwd = "adminpwd.dat";
my $setupfile = "hist_setup.pl";
my $version = "2.008";
my $lastupdatedyear = "2008";
my $script = basename($0);
my $charset = "Shift_JIS";
# Language 0:Japanese 1:English
my $lang = 0;
### デフォルト設定 ###
# data file
our $datafile = 'history.dat';
# Title
our $title = 'ヒストリー2';
# Delete page title
our $deltitle = 'ヒストリー削除';
# 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 = '80';
# Display order 0:From new, 1:From old
our $order = 0;
# Date display 0:年月日 1:年月のみ 2:年のみ
our $date_disp = 0;
# Header
our $headers = '日付,コメント1,コメント2';
# 書き込み別ページ 1:on 0:off
our $sep_wrpage = 0;
# body_head
our $body_head = "
$title
";
# body_tail
our $body_tail = '
';
# mode 0:admin only, 1:user mode
our $user_mode = 0;
# Style Sheet
our $style_sheet_en = 0;
our $style_sheet = '
';
# Head insert
our $head_insert_en = 0;
our $head_insert = '
';
# Offset from GMT, Japan:+9
our $offset = 9;
#######################
require $setupfile if (-e "$setupfile");
my $LOCK_EX = 2;
my $now = time + $offset*3600;
my $q = new CGI;
my $cgierror = $q->cgi_error;
&error($cgierror) if ($cgierror);
my $mode = $q->param('mode');
if (! -e "$adminpwd"){
if ($mode eq 'wradminpwd'){
&wradminpwd;
} else {
&setadminpwd;
}
}
my @header = split /,/, $headers;
my $cols = $#header + 1;
if ($mode eq 'write'){
&histwrite;
} elsif ($mode eq 'delpage'){
&delpage;
} elsif ($mode eq 'delete'){
&histdelete;
} elsif ($mode eq 'modify'){
&histwrite;
} elsif ($mode eq 'setup'){
&setup;
} elsif ($mode eq 'setupwrite'){
&setupwrite;
} elsif ($mode eq 'wrform'){
&wrform;
} else {
&display;
}
sub sortdays {
my $histdata = @_[0];
my @alist = @{$histdata->{$a}};
my @blist = @{$histdata->{$b}};
return Delta_Days(@alist[1 .. 3],@blist[1 .. 3]);
}
sub display {
my $histdata = &getdata;
my @regtime = keys %{$histdata};
@regtime = sort {sortdays($histdata)} @regtime;
@regtime = reverse @regtime if ($order == 1);
&htmlhead($title);
print "$body_head\n";
my ($d_dev,$d_ino,$d_mode,$d_nlink,$d_uid,$d_gid,$d_rdev,$d_size,$d_atime,$d_mtime,$d_ctime,$d_blksize,$d_blocks)=stat("$datafile");
my ($ssec,$smin,$shour,$sday,$smon,$syear,$swday,$syday,$sisdst)=gmtime($d_mtime+$offset*3600);
$syear += 1900 if ($syear < 1900);
$smon++;
print "
\n";
print "$backlink_name \n" if ($backlink_en);
print "${syear}年${smon}月${sday}日更新\n";
print "
\n";
print "
\n";
print "";
foreach (@header){
print "$_ ";
}
print " \n";
# ($id,$dyear,$dmonth,$dday,$dhour,$dmin,$dtime,$dnow,$dpwd,$host,$ip,$dcomment);
foreach (@regtime){
print "\n";
print "";
print "$histdata->{$_}[1]年";
print "$histdata->{$_}[2]月" if ($date_disp == 0 or $date_disp == 1);
print "$histdata->{$_}[3]日" if ($date_disp == 0);
print " \n";
my @comarray = split /<>/, $histdata->{$_}[11];
for (my $i=0;$i<$cols-1;$i++){
print "";
# print "$comarray[$i]:$#comarray:$histdata->{$_}[11]";
if ($comarray[$i] eq ''){
print " ";
} else {
print "$comarray[$i]";
}
print " \n";
}
print " \n";
}
print "
\n";
&wrform unless ($sep_wrpage);
print "$body_tail\n";
print "
\n";
print "";
if ($sep_wrpage){
print "登録ページ \n";
}
print "削除変更ページ \n";
print "";
print "管理用 \n";
print "
\n";
&htmltail;
}
sub wrform {
if ($sep_wrpage and $mode eq 'wrform'){
&htmlhead($title);
print "$title書き込み \n";
print "$titleへ
\n";
}
my ($sec,$min,$hour,$day,$mon,$year,$wday,$yday,$isdst)=gmtime($now);
$mon++;
$year += 1900 if ($year < 1900);
print "
\n";
print "";
print "\n";
print " \n";
print "
\n";
&htmltail if ($sep_wrpage and $mode eq 'wrform');
}
sub delpage {
my $histdata = &getdeldata;
my @regtime = @{$histdata};
@regtime = sort {Delta_Days($a->[1],$a->[2],$a->[3],$b->[1],$b->[2],$b->[3])} @regtime;
@regtime = reverse @regtime if ($order == 1);
&htmlhead($deltitle);
print "$deltitle \n";
my ($d_dev,$d_ino,$d_mode,$d_nlink,$d_uid,$d_gid,$d_rdev,$d_size,$d_atime,$d_mtime,$d_ctime,$d_blksize,$d_blocks)=stat("$datafile");
my ($ssec,$smin,$shour,$sday,$smon,$syear,$swday,$syday,$sisdst)=gmtime($d_mtime+$offset*3600);
$syear += 1900 if ($syear < 1900);
$smon++;
print "\n";
print "${title}へ \n";
print " \n";
print "
\n";
my $headcols = $cols + 4;
print "\n";
print "";
foreach (@header){
print "$_ ";
}
print " ";
print " ";
print "";
print " \n";
print " ↑パスワード\n";
print " ";
print " \n";
# ($id,$dyear,$dmonth,$dday,$dhour,$dmin,$dtime,$dnow,$dpwd,$host,$ip,$dcomment);
foreach my $refkey (@regtime){
print "\n";
print "";
print "$refkey->[1]年";
print "$refkey->[2]月";
print "$refkey->[3]日";
print " \n";
my @comarray = split /<>/, $refkey->[11];
for (my $i=0;$i<$cols-1;$i++){
print "";
if ($comarray[$i] eq ''){
print " ";
} else {
print "$comarray[$i]";
}
print " \n";
}
print "";
print " [0]\" size=10>\n";
print " ";
print "";
print " [0]\" value=\"削除\">\n";
print " ";
print "";
print " [0]\" value=\"変更\">\n";
print " ";
print "";
print " [0]\" value=\"on\">\n";
print " ";
print " \n";
}
print "
\n";
print "削除・変更したい行のテキストボックスににパスワードを入れて削除、変更ボタンをクリックしてください。";
print " \n";
print "
\n";
&htmltail;
}
sub histdelete {
if ($q->param('delete_checked') ne ''){
&delete_checked;
}
my ($fid,$fyear,$fmonth,$fday,$fhour,$fmin,$ftime,$fnow,$fpwd,$fhost,$fip,$fcomment);
open (FILE, "< $datafile") or &error("${datafile}が開けません。");
while (){
my @line = split /,/;
my $modname = 'mod_' . $line[0];
if ($q->param($modname) ne ''){
close(FILE);
&histmodify(@line);
}
}
close(FILE);
my @tmparray = ();
open (DATAFILE, "+< $datafile") or &error("${datafile}が開けません。");
flock DATAFILE, $LOCK_EX;
while (){
chomp;
($fid,$fyear,$fmonth,$fday,$fhour,$fmin,$ftime,$fnow,$fpwd,$fhost,$fip,$fcomment) = split /,/;
my $subname = 'sub_' . $fid;
my $pwdname = 'pwd_' . $fid;
my $inpwd = $q->param($pwdname);
if (!$q->param($subname)) {
push @tmparray, "$fid,$fyear,$fmonth,$fday,$fhour,$fmin,$ftime,$now,$fpwd,$fhost,$fip,$fcomment\n";
} elsif (! &checkcrypt($inpwd,$fpwd)) {
close(DATAFILE);
&error('パスワードが違います。');
}
}
truncate DATAFILE, 0;
seek DATAFILE, 0, 0;
print DATAFILE @tmparray;
close(DATAFILE);
print "Location: $script\n\n";
exit;
}
sub delete_checked {
my @tmparray = ();
open (DATAFILE, "+< $datafile") or &error("${datafile}が開けません。");
flock DATAFILE, $LOCK_EX;
my $inpwd = $q->param('pwd_checked');
while (){
chomp;
my ($fid,$fyear,$fmonth,$fday,$fhour,$fmin,$ftime,$fnow,$fpwd,$fhost,$fip,$fcomment) = split /,/;
my $chkname = 'chk_' . $fid;
if ($q->param($chkname) ne 'on') {
push @tmparray, "$fid,$fyear,$fmonth,$fday,$fhour,$fmin,$ftime,$now,$fpwd,$fhost,$fip,$fcomment\n";
} elsif (! &checkcrypt($inpwd,$fpwd)) {
close(DATAFILE);
&error("「$fyear年$fmonth月$fday日:$fcomment」のパスワードが違います。");
}
}
truncate DATAFILE, 0;
seek DATAFILE, 0, 0;
print DATAFILE @tmparray;
close(DATAFILE);
print "Location: $script\n\n";
exit;
}
sub histmodify {
my ($fid,$fyear,$fmonth,$fday,$fhour,$fmin,$ftime,$fnow,$fpwd,$fhost,$fip,$fcomment) = @_;
my $pwdname = 'pwd_' . $fid;
my $inpwd = $q->param($pwdname);
if (! &checkcrypt($inpwd,$fpwd)) {
&error('パスワードが違います。');
}
&htmlhead('変更');
print "\n";
&htmltail;
exit;
}
sub check_indate {
my ($year,$month,$day) = @_;
if (($year =~ /\D/ or $month =~ /\D/ or $day =~ /\D/) or
(! check_date($year,$month,$day))){
&error("日付が適切ではありません。${year}年${month}月${day}日")
}
}
sub histwrite {
my $year = $q->param('year');
my $month = $q->param('month');
my $day = $q->param('day');
&check_indate($year,$month,$day);
$year = sprintf("%d",$year);
$month = sprintf("%d",$month);
$day = sprintf("%d",$day);
my $hour = 0;
my $min = 0;
my @comment;
for (my $i=0;$i<$cols-1;$i++){
$comment[$i] = $q->param("comment_$i");
}
my $pwd = $q->param('pwd');
my $infid = $q->param('id');
my $id;
my $regtime;
my $host;
my $ip;
my $encpwd;
for (my $i=0;$i<$cols-1;$i++){
$comment[$i] =~ s/,/,/g;
$comment[$i] =~ s/\r\n/ /g;
$comment[$i] =~ s/\r/ /g;
$comment[$i] =~ s/\n/ /g;
$comment[$i] =~ s/<(\s*\/? *t)/<$1/ig; # Do not allow table tag
$comment[$i] =~ s/<(\s*plaintext)/<$1/ig;
$comment[$i] =~ s/<(\s*script)/<$1/ig;
$comment[$i] =~ s/<(\s*input)/<$1/ig;
$comment[$i] =~ s/<(\s*pre)/<$1/ig;
$comment[$i] =~ s/<(\s*embed)/<$1/ig;
$comment[$i] =~ s/<>/<>/ig;
}
$host = $ENV{'REMOTE_HOST'};
$ip = $ENV{'REMOTE_ADDR'};
# This colum is not used.
# $regtime = timegm(0,$min,$hour,$day,$monthn1,$year);
$regtime = 0;
$encpwd = &makecrypt($pwd);
&error('管理人オンリーモードなので管理人しか書き込めません。')
if ($user_mode == 0 and !&checkadmin($pwd));
# &error('パスワードが違います。')
# if ($user_mode == 1 and !&checkcrypt($pwd,$encpwd));
if ($mode eq 'modify'){
my @tmparray = ();
open (DATAFILE, "+< $datafile") or &error("${datafile}が開けません。");
flock DATAFILE, $LOCK_EX;
while (){
chomp;
my ($fid,$fyear,$fmonth,$fday,$fhour,$fmin,$ftime,$fnow,$fpwd,$fhost,$fip,$fcomment) = split /,/;
my $modname = 'mod_' . $fid;
if ($fid != $infid) {
push @tmparray, "$fid,$fyear,$fmonth,$fday,$fhour,$fmin,$ftime,$now,$fpwd,$fhost,$fip,$fcomment\n";
} elsif (&checkcrypt($pwd,$fpwd)) {
my $jcomment = join '<>', @comment;
push @tmparray, "$fid,$year,$month,$day,$hour,$min,$regtime,$now,$encpwd,$host,$ip,$jcomment\n";
} else {
close(DATAFILE);
&error("$fid:$infidパスワードが違います。");
}
}
truncate DATAFILE, 0;
seek DATAFILE, 0, 0;
print DATAFILE @tmparray;
close(DATAFILE);
} else {
if (open DATA, "< $datafile"){
while (){
my @dummy;
($id,@dummy) = split /,/;
}
close DATA;
$id++;
} else {
$id = 1;
}
open (FILE, ">> $datafile") or &error("${datafile}が開けません。");
# ($id,$dyear,$dmonth,$dday,$dhour,$dmin,$dtime,$dnow,$dpwd,$host,$ip,$dcomment);
flock FILE, $LOCK_EX;
my $jcomment = join '<>', @comment;
print FILE "$id,$year,$month,$day,$hour,$min,$regtime,$now,$encpwd,$host,$ip,$jcomment\n";
close(FILE);
}
print "Location: $script\n\n";
}
sub sortdata {
my @alist = split /,/, $a;
my @blist = split /,/, $b;
return Delta_Days(@blist[1 .. 3],@alist[1 .. 3]);
}
sub getdata {
my %histdata;
my @dataarray;
my $id;
my $host;
my $ip;
my $id;
my @alldata;
if (open DATA, "< $datafile"){
@alldata = ;
close DATA;
}
foreach (sort sortdata @alldata) {
chomp;
# ($id,$dyear,$dmonth,$dday,$dhour,$dmin,$dtime,$dnow,$dpwd,$host,$ip,$dcomment);
@dataarray = split /,/;
next if ($dataarray[11] eq '');
my $key;
if ($date_disp == 0) {
$key = join '_', @dataarray[1 .. 3];
} elsif ($date_disp == 1) {
$key = join '_', @dataarray[1 .. 2];
} elsif ($date_disp == 2){
$key = $dataarray[1];
}
if (exists $histdata{$key}){
my @orgcomment = split /<>/, $histdata{$key}->[$#dataarray];
my @newcomment = split /<>/, $dataarray[11];
my $max;
if ($#orgcomment >= $#newcomment){
$max = $#orgcomment;
} else {
$max = $#newcomment;
}
my @replacecomment;
for (my $i=0;$i<=$max;$i++){
if ($orgcomment[$i] ne ''and $newcomment[$i] ne ''){
$replacecomment[$i] = $orgcomment[$i] . " " . $newcomment[$i];
} elsif ($orgcomment[$i] eq '' and $newcomment[$i] ne ''){
$replacecomment[$i] = $newcomment[$i];
} else {
$replacecomment[$i] = $orgcomment[$i];
}
}
my $replacecomment = join '<>', @replacecomment;
$histdata{$key} = [
@dataarray[0 .. $#dataarray-1],
$replacecomment,
];
} else {
$histdata{$key} = [@dataarray];
}
}
return (\%histdata);
}
sub getdeldata {
my @histdata;
my @dataarray;
if (open DATA, "< $datafile"){
@histdata = ();
while (){
chomp;
# ($id,$dyear,$dmonth,$dday,$dhour,$dmin,$dtime,$dnow,$dpwd,$host,$ip,$dcomment);
@dataarray = split /,/;
next if ($dataarray[11] eq '');
push(@histdata, [@dataarray]);
}
close DATA;
return (\@histdata);
}
}
sub htmlhead {
my $title = shift;
my $bgimage;
if ($bgimage_en == 1){
$bgimage = "background=\"$bgimage_file\"";
} else {
$bgimage = "bgcolor=\"$bgcolor\"";
}
# print "Content-type:text/html\n\n";
print $q->header(-charset=>"$charset");
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 htmltail {
my $mysite = ('http://www.hidekik.com/','http://www.hidekik.com/en/')[$lang];
print "history2.cgi Ver. $version
\n";
print "\n";
print "
\n";
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 checkcrypt {
my ($pwd,$encpwd)=@_;
return(crypt($pwd,$encpwd) eq "$encpwd" or &checkadmin($pwd));
}
sub checkadmin {
my $pwd = shift;
if (open(FILE,"< $adminpwd")){
my $filepwd = ;
close(FILE);
my $inpwd = crypt($pwd,$filepwd);
return ("$inpwd" eq "$filepwd");
} else {
&error('パスワードファイルが存在しません');
}
}
sub setadminpwd {
&htmlhead('管理者用パスワードを入力してください');
print "\n";
if ($mode eq 'setup'){
print "管理者用パスワードを入力してください。 \n";
print " \n";
print " \n";
print " \n";
} else {
print "管理者用パスワードを設定してください。 \n";
print " \n";
print " \n";
print " \n";
}
&htmltail;
}
sub wradminpwd {
my $plain = $q->param('pwd');
my $passwd = &makecrypt($plain);
if (open(FILE,"> $adminpwd")){
print FILE "$passwd";
close(FILE);
} else {
&error('パスワードファイル作成に失敗しました');
}
print "Location: $script\n\n";
}
sub error {
my ($msg) = shift;
&htmlhead($msg);
print "$msg \n";
&htmltail;
exit;
}
sub setup {
my $inpwd = $q->param('pwd');
&setadminpwd if ($inpwd eq '');
&error('管理用パスワードが違います。') unless &checkadmin($inpwd);
&htmlhead('管理用セットアップ');
my %check;
$check{bgimage}[$bgimage_en] = "checked";
$check{backlink}[$backlink_en] = "checked";
$check{order}[$order] = "checked";
$check{date_disp}[$date_disp] = "checked";
$check{user_mode}[$user_mode] = "checked";
$check{style_sheet}[$style_sheet_en] = "checked";
$check{head_insert}[$head_insert_en] = "checked";
$check{sep_wrpage}[$sep_wrpage] = "checked";
print "\n";
print " \n";
print " \n";
print <
数字やカラー指定は必ず半角で指定してください。全角やブランクだとCGIが起動しなくなります。 万一間違って全角で書いてしまった場合は、${setupfile}をエディタで開きその場所を半角に正しく修正してください。それで直ります。
データファイル
タイトル
削除ページタイトル
バックグランド
画像を使う
カラー設定にする
画像を使う場合の画像ファイル
カラー設定の場合のカラー番号(白:#ffffff 又は white)
トップへのリンク
表\示
非表\示
リンク名
URL
表\示幅
ブラウザ全体の %
表\示する順序
新しい順
古い順
年月日表\示
年月日
年月のみ
年のみ
カラムのタイトル
半角カンマで区切ってください。日付と最低もう一つは指定してください。指定した数だけコメントがその日に記入でいるようになります。
管理人モード
管理人オンリーモード
ユーザーモード
管理人オンリーでは管理人しか書き込めません。ユーザーモードでは誰でも書き込めます。
書き込み別ページ
書き込みフォームは表\示ページの下
書き込みページは別ページ
ページ上部
ページ上部に表\示させるものをHTML表\記
$body_head
ページ下部
ページ下部に表\示させるものをHTML表\記
$body_tail
スタイルシート
有効
無効
$style_sheet
<head>内挿入文
有効
無効
HTML書式
ポップアップ広告やJavascript、<META>を挿入したい場合にここに記述する。
以下の記述が<head>〜</head>内に挿入される。
$head_insert
時間設定
GMTより 時間(日本:+9時間)
END
print " ";
print " ";
&htmltail;
}
sub setupwrite {
my %in = $q->Vars;
open (SETUP, "> $setupfile") or &error("${setupfile}が開けません。");
print SETUP <