#!/usr/local/bin/perl # ############################################################# ### ### CGI日記帳 Ver.1.0 ### [1/2] 書き込み用 ### ### (C) 2001 Sentora Dori ### http://www.lowpower.iis.u-tokyo.ac.jp/~hat ### ############################################################# ##### プログラム名(変更禁止) ##### $programname = 'diary'; #################### 変数設定(ここから) #################### # 日記データをおくディレクトリのパス $databasedir = '.'; # 日記の最大の長さ $maxlength = 4000; # パスワードファイルのあるディレクトリのパス $passfiledir = '.'; # パスワードファイル名 $passfile = 'SDpass.txt'; # 管理用パスワード (省略するとパスワードファイルからパスワードを探す) $pass = ''; # post: postメソッド get: getメソッド $method = 'post'; # 2: flockを使用する 1: ロックファイルでロックする 0: ロックしない (通常は変更必要なし) $uselock = 2; # 漢字コード(sjis or jis or euc) $kanjicode = 'sjis'; # 漢字ライブラリのファイル名 $jcodelib ='./jcode.pl'; #################### 変数設定(ここまで) #################### # このプログラムのタイトル $title = '日記帳'; # パスワードファイルであることを示すサイン $passsign = 'SDpassfile'; # flockを使用しないロックのときのロックファイル名のベース (変更必要なし) $baselockfile = './SDlock'; # このファイル名 $thisurl = 'SDdiarywrite.cgi'; #----- 漢字コード設定 ----- require "$jcodelib"; if ($kanjicode eq 'sjis') { $contenttype = ""; # $contenttype = ""; } elsif ($kanjicode eq 'euc') { $contenttype = ""; # $contenttype = ""; } elsif ($kanjicode eq 'jis') { $contenttype = ""; } ##### format ##### @week = ("日", "月", "火", "水", "木", "金", "土"); @maxday = (31,29,31,30,31,30,31,31,30,31,30,31); $passfile = "$passfiledir/$passfile"; &init_form($kanjicode); #----- 変数入力 ----- if ($form{'diaryname'}) { $diaryname = $form{'diaryname'}; $datafile = "$databasedir/$diaryname.txt"; } if ($form{'todiary'}) { $todiary = $form{'todiary'}; $movetofile = "$databasedir/$todiary.txt"; } $command = $form{'command'}; $diaryid = $form{'diaryid'}; $message = $form{'message'}; $inputpass = $form{'inputpass'}; $inputdate = $form{'inputdate'}; $inputyear = $form{'inputyear'}; $inputmon = $form{'inputmon'}; $inputday = $form{'inputday'}; $message = &norm_input($message); ##### branch(入力されたコマンドによる分岐) ##### if ($command eq 'read') { &do_checkpass; &do_read; } elsif ($command eq 'write') { &do_checkpass; &do_write; } elsif ($command eq 'delete') { &do_checkpass; &do_delete; } elsif ($command eq 'modify') { &do_checkpass; &do_modify; } elsif ($command eq 'modify2') { &do_checkpass; &do_modify2; } elsif ($command eq 'cutolddiary') { &do_checkpass; &do_cutolddiary; } elsif ($command eq 'checkpass') { &do_checkpass; &do_read; } else { &show_inputpass; } exit(0); ###################### ##### subroutine ##### ###################### ###### do(コマンド実行) ##### #----- アンケート結果表示 ----- sub do_read { &show_diary; } #----- 書き込む ----- sub do_write { &check_inputdata; &open_file(TXT, "+<$datafile", "[no data file]"); &lock_file(TXT); @txt = ; # リロードかどうかチェックし連続書き込みを防止する ($print_id, $print_time, $print_message) = split(/,/, $txt[0]); chomp($print_message); if ($message eq $print_message) { &unlock_file(TXT); &close_file(TXT); &show_diary; return; } &get_date_string; if ($inputdate eq 'ON') { $datestr = "$inputyear-$inputmon-$inputday $date2"; } else { $datestr = "$date1 $date2"; } # 書き込み位置検出 $id = 1; for ($i = 0; $i < @txt; $i++) { ($bakid) = split(/,/, $txt[$i]); if ($bakid >= $id) { $id = $bakid + 1; } } $writeline = "$id,$datestr,$message\n"; unshift(@txt, $writeline); seek(TXT, 0, 0); print TXT @txt; &unlock_file(TXT); &close_file(TXT); &show_diary; } #----- 削除する ----- sub do_delete { &find_message; &open_file(TXT, "+<$datafile", "[no data file]"); &lock_file(TXT); @txt = ; splice(@txt, $index, 1); seek(TXT, 0, 0); print TXT @txt; truncate(TXT, tell(TXT)); &unlock_file(TXT); &close_file(TXT); &show_diary; } #----- 修正する ----- sub do_modify { &find_message; &show_diary; } sub do_modify2 { &find_message; &check_inputdata; &open_file(TXT, "+<$datafile", "[no data file]"); &lock_file(TXT); @txt = ; ($print_id, $print_date, $print_message) = split(/,/, $txt[$index]); chomp($print_message); if ($inputdate eq 'ON') { ($date1, $date2) = split(/\s/, $print_date); $datestr = "$inputyear-$inputmon-$inputday $date2"; } else { $datestr = $print_date; } $writeline = "$print_id,$datestr,$message\n"; $txt[$index] = $writeline; seek(TXT, 0, 0); print TXT @txt; truncate(TXT, tell(TXT)); &unlock_file(TXT); &close_file(TXT); &show_diary; } #----- 古い日記を別のファイルに移動 ----- sub do_cutolddiary { &find_message; &open_file(TXT, "+<$datafile", "[no data file]"); &lock_file(TXT); @txt = ; &open_file(TXT2, "+<$movetofile", "移動先のファイルがありません。"); &lock_file(TXT2); @txt2 = ; @cutdiary = splice(@txt, $index); seek(TXT, 0, 0); print TXT @txt; truncate(TXT, tell(TXT)); &unlock_file(TXT); &close_file(TXT); seek(TXT2, 0, 0); print TXT2 @cutdiary; print TXT2 @txt2; &unlock_file(TXT2); &close_file(TXT2); &show_diary; } #----- doルーチンのユーティリティ ----- sub find_message { &print_error("日記番号が入力されていません。") if ($diaryid !~ /^\d+$/); open_file(TXT, "$datafile", "[no data file]"); @txt = ; close_file(TXT); # find message $index = -1; for ($i = 0; $i < @txt; $i++) { ($findid) = split(/,/, $txt[$i]); if ($diaryid == $findid) { $index = $i; last; } } if ($index < 0) { &print_error("指定した日記番号の日記はありません。"); } } sub check_inputdata { if (!$message) { &print_error("文章が入力されていません。"); } if ($maxlength && length($message) > $maxlength) { &print_error("文章が長すぎます。"); } if ($inputdate eq 'ON') { &print_error("年月日の入力が違います。") if ($inputyear !~ /^\d+$/); &print_error("年月日の入力が違います。") if ($inputmon < 1 || $inputmon > 12); &print_error("年月日の入力が違います。") if ($inputday < 1 || $inputday > $maxday[$inputmon-1]); } } #----- パスワードチェック ----- sub do_checkpass { local($i); if ($pass) { return if ($pass eq $inputpass); &print_error("[password mismatch]"); } open(TXT, "$passfile") || die &print_error('[no password file]'); @txt = ; close(TXT); ($sign, $pass) = splice(@txt, 0, 2); chomp($sign); chomp($pass); if ($sign ne $passsign) { &print_error("[not password file]"); } $passmatch = 0; for ($i = 0; $i < @txt; $i++) { ($name, $pass) = split(/,/, $txt[$i]); chomp($pass); if ($name eq $programname) { if ($pass ne crypt($inputpass, $pass)) { &print_error("[$name password mismatch!]"); } else { $passmatch = 1; last; } } } for ($i = 0; $i < @txt; $i++) { ($name, $pass) = split(/,/, $txt[$i]); chomp($pass); if ($name eq 'default') { if ($pass ne crypt($inputpass, $pass)) { &print_error("[default password mismatch!]"); } else { $passmatch = 1; last; } } } &print_error("[no adequate password in password file]") if (!$passmatch); return; } ##### show(HTML表示) ##### #----- HTMLファイルと結果を表示する ----- sub show_diary { if ($diaryname) { &open_file(TXT, "$datafile", "[no data file]"); @txt = ; close_file(TXT); } print "Content-type: text/html\n\n"; print <<"END_HEADER"; $title $title 日記名 END_HEADER return if (!$diaryname); $zenkaku_maxlength = int($maxlength / 2); if ($maxlength) { $print_maxlength = "全角$zenkaku_maxlength文字以内で書いてください。"; } local($sec, $min, $hour, $day, $mon, $year, $weekday) = localtime(time); $year += 1900; $mon++; if ($command eq 'modify') { ($print_id, $date, $print_message) = split(/,/, $txt[$index]); chomp($print_message); $print_message = &unnorm_input($print_message); ($bakdate) = split(/\s/, $date); ($year, $mon, $day) = split(/-/, $bakdate); print <<"END_INPUTFORM_MODIFY"; 修正 (日記番号: $diaryid) 年月日も変更する 年 月 日 $print_message $print_maxlength END_INPUTFORM_MODIFY $print_modify = ""; } else { print <<"END_INPUTFORM_WRITE"; 年月日を指定する 年 月 日 $print_maxlength END_INPUTFORM_WRITE } for ($i = 0; $i < @txt; $i++) { ($print_id, $date, $print_message) = split(/,/, $txt[$i]); ($bakdate) = split(/\s/, $date); ($year, $mon, $day) = split(/-/, $bakdate); $weekday = &getwday($year, $mon, $day); $print_datestr = "$year年$mon月$day日"; print "日記番号: $print_id"; print "$print_datestr$print_message"; print ""; } print <<"END_FOOT"; 削除 日記番号 修正 日記番号 古い日記を別のファイルに移動 (基準となる日記番号と移す先の日記名を入力してください。) 日記番号 番より前の日記を に END_FOOT } #----- パスワード入力画面表示 ----- sub show_inputpass { print "Content-type: text/plain\n\n"; print <<"END_SHOW_INPUTPASS"; $title $title 管理用パスワード入力 パスワード END_SHOW_INPUTPASS } #----- 時刻文字列の取得 ----- sub get_date_string { local($sec, $min, $hour, $day, $mon, $year, $weekday) = localtime(time); $year += 1900; $mon++; if ($hour < 10) { $hour = "0$hour"; } if ($min < 10) { $min = "0$min"; } if ($sec < 10) { $sec = "0$sec"; } $date1 = "$year-$mon-$day"; $date2 = "$hour:$min:$sec"; return } sub getwday { local($year, $mon, $day) = @_; local($wday); if ($month == 1 || $month == 2) { $year--; $month += 12; } $wday = int($year + int($year/4) - int($year/100) +int($year/400) + int((13*$month+8)/5) + $day) % 7; return $wday; } ##### 入出力処理 ###### #----- ブラウザへの入力データの取得 ----- 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); 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 unnorm_input{ local($string) = @_; $string =~ s/&/&/g; # $string =~ s/"/"/g; # $string =~ s/<//g; $string =~ s/,/,/g; $string =~ s/:/:/g; $string =~ s/;/;/g; # $string =~ s/\n/\r\n/g; # $string =~ s/\n/\r/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); }