#!/usr/local/bin/perl
#--------------------------------------#
# スケジュール帳
# 2006/05/30
#--------------------------------------#
#------------------------------------------#
# 設定項目
#------------------------------------------#
#このCGI
$THIS_URL = "./schedule.cgi";
#ログファイル
$LOG_FILE = "./schedule.txt";
#管理者用パスワード
$MASTER_KEY = 'aaaa';
#モード設定(誰でも書ける=0,自分しか書かない=1)
$ACT_MODE = 1;
#ホームURL
$HOME_URL = "../../";
#タイトル
$TITLE_STR = "スケジュール帳";
#TOPページのタイトル
$TOP_PAGE_STR = "スケジュール帳";
#最大登録件数(最大数を越えると古い日付のものから削除されます)
$MAX_DATA_SIZE = 4096;
#何か月分表示するか
$DISP_MONTH_NUM = 10;
#最近の日数を何日分にするか
$NEAR_DATE = 50;
#祝日登録(年月日) 上下ペアになっています。
@HOLIDAY = ( '20060101', '20060109', '20060211', '20060321', '20060429', '20060503', '20060504', '20060505', '20060717', '20060918', '20060923', '20061010', '20061103', '20061123', '20061223');
@HOLIDAY_WHAT = ( '元旦', '成人の日', '建国記念の日', '春分の日', 'みどりの日', '憲法記念日', '国民の休日', 'こどもの日', '海の日', '敬老の日', '秋分の日', '体育の日', '文化の日', '勤労感謝の日', '天皇誕生日');
#ロック用ディレクトリ
$LOCKDIR = './lock/';
#クッキーの名前
$COOKIE_NAME = 'schedule';
#クッキーの期限
$COOKIE_LIMIT = 'Thu, 1-Jan-2030 00:00:00 GMT;';
#スタイルシート
$STYLEFILE = '../../style.css';
#------------------------------------------#
# 設定は以上まで
#------------------------------------------#
#ver1.0 2006/05/30 公開。
&Main;
#------------------------------------------#
# メイン
#------------------------------------------#
sub Main{
#初期化
&Initialize;
#ヘッダ
&Head;
#タイトル
print "
$TOP_PAGE_STR
\n";
#各ページへ分岐
if( $in{"page"} eq "schedule" ){
#ナビゲーション
&CalNavi;
#スケジュールページ
&DispCalen( $in{"year"}, $in{"month"} );
}
#登録ページ
elsif( $in{"page"} eq "regist_page" ){
&RegistPage;
}
#登録NEXT
elsif( $in{"page"} eq "done_regist" ){
&DoneRegistPage;
}
#記事表示ページ
elsif( $in{"page"} eq "look" ){
&LookData( $in{id} );
}
#記事削除
elsif( $in{"page"} eq "delete" ){
&DeleteData( $in{id}, $in{password} );
}
else{
#ナビゲーション
&CalNavi;
#スケジュールページ(最近$NEAR_DATE日)
&DispCalen( $gl_year, $gl_month, $gl_day, $NEAR_DATE );
}
#フッタ
&Foot;
}
#------------------------------------------#
# 初期化
#------------------------------------------#
sub Initialize{
#フォームデータ取得
&GetFormData;
#現在の日時取得
&GetNowDate;
#クッキーをセット
if( $in{"page"} eq "done_regist" ){
print "Set-Cookie: $COOKIE_NAME=name:$in{name},password:$in{password}; expires=$COOKIE_LIMIT\n";
}
#クッキーを取得
&GetCookie;
}
#------------------------------------------#
# ヘッダ
#------------------------------------------#
sub Head{
print "Content-type: text/html\n";
print "\n";
print "\n\n";
print "$TITLE_STR\n";
print "\n";
print "\n";
print "\n";
print "\n";
print "Home
\n";
}
#------------------------------------------#
# フッタ
#------------------------------------------#
sub Foot{
print "\n";
print "\n";
}
#------------------------------------------#
# 登録ページ
#------------------------------------------#
sub RegistPage{
print "登録ページ
\n";
#投稿者情報の入力
&FormUserInfo;
}
#------------------------------------------#
# 投稿者情報の入力
#------------------------------------------#
sub FormUserInfo{
#日付情報分解
my $year = substr( $in{date}, 0, 4 );
my $month = substr( $in{date}, 4, 2 );
my $date = substr( $in{date}, 6, 2 );
print "
\n";
}
#------------------------------------------#
# 登録完了ページ
#------------------------------------------#
sub DoneRegistPage{
$ErrorFlag = 0;
#項目チェック
if( !$in{name} ){ &RegistError("名前を入力して下さい。"); }
if( !$in{password} ){ &RegistError("パスワードを入力して下さい。"); }
#自分しか書かない場合
if( $ACT_MODE == 1 ){
#パスワード照合
if( $MASTER_KEY ne $in{password} ){
&RegistError("パスワードが違います。");
}
}
#エラーがなければログ書き込み開始
if( !$ErrorFlag ){
&WriteLogData;
print "登録完了\n";
print "更新\n";
}else{
print "戻って記入し直して下さい。\n";
}
}
#------------------------------------------#
# 登録エラー
#------------------------------------------#
sub RegistError{
print "$_[0]
\n";
$ErrorFlag = 1;
}
#------------------------------------------#
# ログデータ書き込み
#------------------------------------------#
sub WriteLogData{
#まずはログファイル読み込み
&ReadLogData;
#新規ID取得(一番数の大きいもの)
my $max = 0;
foreach( @LogData ){
my( $id ) = split( /<>/, $_ );
if( $id > $max ){ $max = $id; }
}
$max++;
$myid = $max;
#パスワード暗号化
$enckey = &GetEnckey( $in{password} );
#追加ログフォーマット
my $push_log = "$myid<>$in{date}<>$in{name}<>$enckey<>$in{open_hour}<>$in{close_hour}<>$in{msg}\n";
$find_flag = 0;
#日付でソートされてるので、挿入箇所を探す
for( $i = 0; $i < scalar( @LogData ); $i++ ){
#ログ分割
my( $id, $date_str, $name, $password, $mail, $homeurl, $time, $etc2, $etc3, $msg ) = split( /<>/, $LogData[ $i ] );
#$date_strは数値として見れるので、それの大小で判別
#もし大きくなればその手前に挿入
if( $in{date} > $date_str ){
splice( @LogData, $i, 0, $push_log );
$find_flag = 1;
last;
}
}
#見つからなかった場合は、最後尾に追加
if( !$find_flag ){
push( @LogData, $push_log );
}
#ログ書き込み
#ロック
my $lflag=0;
for( $i = 0; $ i < 10; $i++ ){
#ロック用ディレクトリ作成
if( mkdir( $LOCKDIR, 0755 ) ){
$lflag = 1;
last;
}else{
#1秒待つ
sleep(1);
}
}
if( !$lflag ){
&Error("ロックに失敗。しばらく待ってから再び投稿して下さい。\n
しばらく待っても改善されない時は管理者へ連絡して下さい。");
}
#記事数が一定以上を越えると過去ログに。
if( scalar( @LogData ) > $MAX_DATA_SIZE ){
pop( @LogData );
}
open( OUT, ">$LOG_FILE" );
print OUT @LogData;
close( OUT );
#アンロック
rmdir( $LOCKDIR );
}
#------------------------------------------#
# キーを暗号化して取得
#------------------------------------------#
sub GetEnckey{
my $key = $_[0];
#キーを暗号化
my $xx = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789./";
my $salt = substr( $xx, int(rand(64)), 1 );
$salt .= substr( $xx, int(rand(64)), 1 );
return crypt( $key, $salt );
}
#------------------------------------------#
# カレンダーナビゲーション
#------------------------------------------#
sub CalNavi{
my $month;
my $year;
my $year_str;
#カレンダーナビゲーション
printf( "最近%s日\n", $NEAR_DATE );
$year = $gl_year;
$pre_year = $year; #前回保持
$month = $gl_month;
for( $i = 0; $i < $DISP_MONTH_NUM; $i++){
#年が切り替わった時
if( $pre_year != $year ){
$year_str = "$year年";
}else{
$year_str = "";
}
printf(" %s%s月\n", $year, $month, $year_str, $month );
$pre_year = $year; #前回保持
#次の月へ
$month++;
if( $month > 12 ){
$month = 1;
$year++;
}
}
}
#------------------------------------------#
# フォームデータの受け取り
#------------------------------------------#
sub GetFormData{
my( $buffer,@pairs );
if( $ENV{REQUEST_METHOD} eq 'POST' ){
read( STDIN, $buffer, $ENV{CONTENT_LENGTH} );
}else{
$buffer = $ENV{QUERY_STRING};
}
@pairs = split( /&/, $buffer );
foreach( @pairs ){
my( $name, $value ) = split( /=/, $_ );
#変換
$value =~ tr/+/ /;
$value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C",hex($1))/eg;
$value =~ s/&/&/g;
$value =~ s/"/"/g;
$value =~ s/</g;
$value =~ s/>/>/g;
$value =~ s/\r\n/\n/g;
$value =~ s/\n/
/g;
#ハッシュに
$in{$name} = $value;
}
}
#-----------------------------------------------------#
# 現在の日時取得
#-----------------------------------------------------#
sub GetNowDate{
#グローバル変数
( $gl_sec, $gl_min, $gl_hour, $gl_day, $gl_month, $gl_year ) = localtime( time );
$gl_year += 1900;
$gl_month++;
}
#-----------------------------------------------------#
# その年と月のカレンダー表示
# @par 2 年 月
#-----------------------------------------------------#
sub DispCalen{
my $year = $_[0];
my $month = $_[1];
my $start_date = $_[2]; #表示開始日
my $disp_date_num = $_[3]; #表示日数
my $date; #日
my ( $i, $k, $dates, $day_str, $next_flag );
my @days = ( '日', '月', '火', '水', '木', '金', '土' );#曜日表記
my $day; #曜日
printf( "%d年%d月
\n", $year, $month );
$day = &GetDayOfWeek( $year, $month, 1 ); #その月の最初の日の曜日
$dates = &GetDaysOfMonth( $year, $month ); #その月の日数
$next_flag = 0;
#最近表示の場合
if( $start_date ){
$end_date = $start_date + ($disp_date_num-1);
if( $end_date > $dates ){
$end_date = $dates;
#繰り越しフラグ
$next_flag = 1;
}
}else{
$start_date = 1;
$end_date = $dates;
}
#ログファイルからデータ取得
&ReadLogData;
$day = &GetDayOfWeek( $year, $month, $start_date ); #最初の日の曜日
for ($i = $start_date; $i <= $end_date; $i++) { #月の最終日まで表示
$date = $i;
#日にち文字列
$date_str = sprintf( "%04d%02d%02d", $year, $month, $i );
#曜日の色
$day_str = '';
$HOLIDAY_str = '';
#祝日かどうか
for( $j = 0; $j < scalar( @HOLIDAY ); $j++ ){
if( $date_str eq $HOLIDAY[ $j ] ){
$day_str = "$days[$day]";
$HOLIDAY_str = "$HOLIDAY_WHAT[ $j ]";
last;
}
}
#祝日でない場合
if( $day_str eq '' ){
if( $day == 0 ){
$day_str = "$days[$day]";
}elsif( $day == 6 ){
$day_str = "$days[$day]";
}else{
$day_str = "$days[$day]";
}
}
printf("%02d日(%s) %s\n",
$date, $day_str, $HOLIDAY_str );
#その日はスケジュール登録あるかチェック
&CheckRegist( $date_str );
print "
\n";
#曜日加算
$day = ($day + 1) % 7;
}
#繰り越しフラグがある場合
if( $next_flag ){
#来月を求める
$month++;
if( $month > 12 ){
$year++;
$month = 1;
}
#日数を求める
$day_num = $disp_date_num - ($dates - $start_date + 1);
#サブルーチン呼び出し
&DispCalen( $year, $month, 1, $day_num );
}
}
#-----------------------------------------------------#
# ログファイルからデータ取得
#-----------------------------------------------------#
sub ReadLogData{
#ログファイルオープン
open( IN, $LOG_FILE) or &Error("$LOG_FILEが開けません。\n");
@LogData = ;
close( IN );
}
#-----------------------------------------------------#
# スケジュール登録あるか?
# @par 2 年 月
#-----------------------------------------------------#
sub CheckRegist{
$in_date = $_[0];
foreach( @LogData ){
my( $id, $date_str, $name, $password, $open_time, $close_time, $msg ) = split(/<>/,$_);
#HIT
if( $date_str eq $in_date ){
#日時を分解
$year = substr( $date_str, 0, 4 );
$month = substr( $date_str, 4, 2 );
$date = substr( $date_str, 6, 2 );
printf( "
\n %s %s時〜%s時 $msg",
$name, $open_time, $close_time );
}
}
}
#-----------------------------------------------------#
# 記事表示
# @par 1 ID
#-----------------------------------------------------#
sub LookData{
$lookId = $_[0];
#ログファイルからデータ取得
&ReadLogData;
#ID検索
foreach( @LogData ){
my( $id, $date_str, $name, $password, $open_time, $close_time, $etc2, $etc3, $msg ) = split(/<>/,$_);
#HIT
if( $id eq $lookId ){
#日時を分解
$year = substr( $date_str, 0, 4 );
$month = substr( $date_str, 4, 2 );
$date = substr( $date_str, 6, 2 );
printf( "%s %s時〜%s時
\n", $name, $open_time, $close_time );
print "\n";
last;
}
}
}
#-----------------------------------------------------#
# 記事削除
# @par 2 ID Pass
#-----------------------------------------------------#
sub DeleteData{
my $myid = $_[0];
my $mypass = $_[1];
#ログファイルからデータ取得
&ReadLogData;
#ID検索
for( $i = 0; $i < scalar( @LogData ); $i++ ){
my( $id, $date_str, $name, $password ) = split( /<>/, $LogData[ $i ] );
#HIT
if( $id eq $myid ){
#パスワードが正しいかどうか
if( $password eq crypt( $mypass, $password ) ){
#削除
splice( @LogData, $i, 1 );
}else{
&Error("パスワードが正しくありません。");
}
last;
}
}
#書き込み
#ロック
my $lflag=0;
for( $i = 0; $ i < 10; $i++ ){
#ロック用ディレクトリ作成
if( mkdir( $LOCKDIR, 0755 ) ){
$lflag = 1;
last;
}else{
#1秒待つ
sleep(1);
}
}
if( !$lflag ){
&Error("ロックに失敗。しばらく待ってから再び投稿して下さい。\n
しばらく待っても改善されない時は管理者へ連絡して下さい。");
}
open( OUT, ">$LOG_FILE" );
print OUT @LogData;
close( OUT );
#アンロック
rmdir( $LOCKDIR );
print "削除完了\n";
print "更新\n";
}
#-----------------------------------------------------#
# エラーを出力して強制終了
# @par 1 出力文字列
#-----------------------------------------------------#
sub Error{
print @_;
&Foot;
exit;
}
#-----------------------------------------------------#
# 西暦年月日からその曜日を算出する
# @par 3 年月日
#-----------------------------------------------------#
sub GetDayOfWeek{
my( $year, $month, $day ) = @_;
if( $month == 1 || $month == 2 ){
$year--;
$month += 12;
}
return( $year + int($year/4) - int($year/100) + int($year/400) + int((13*$month+8)/5) + $day ) % 7;
}
#-----------------------------------------------------#
# 西暦年号と月からその月の日数を算出する
# @par 2 年月
#-----------------------------------------------------#
sub GetDaysOfMonth{
my( $year, $month ) = @_;
my( @days );
@days = ( 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 );
if ($month < 1 || $month > 12){
$month = 1;
}
#閏年かどうか
if ($month == 2){
return $days[1] + ($year % 4 == 0 && $year % 100 != 0 || $year % 400 == 0);
}
else{
return $days[$month-1];
}
}
#-----------------------------------------------------#
# クッキーの取得
#-----------------------------------------------------#
sub GetCookie{
local( $key, $val );
my @cookies = split( /; /, $ENV{HTTP_COOKIE} );
foreach( @cookies ){
($key,$val) = split( /=/, $_ );
if( $key eq $COOKIE_NAME ){
@cookies = split(/,/,$val);
foreach(@cookies){
($key,$val) = split(/:/,$_);
$val =~ s/%([0-9A-Fa-f][0-9A-Fa-f])/pack("C", hex($1))/eg;
$cookie{$key} = $val;
}
last;
}
}
}