#!/usr/local/bin/perl ;# 簡易BBS type3 version 2.05a フリーソフト ;# ;#《設置条件等》 ;# ;# ・UNIXサーバであること(それ以外のOSでの動作は未確認&非対応) ;# ・SJISで設置すること(それ以外の文字コードでの動作は未確認&非対応) ;# ・標準入力(Method=POST)が使えること ;# ・暗号はDESおよびMD5に対応 ;# ・(推奨)Perl version 5 上で動作させること ;# ・(推奨)GDBM_Fileが使えること ;# ;# This is FreeSoft. ;# Script written by (c) Kazutomo Yanaka ;# Created on: 02/May/96 (Type1) ;# Created on: 28/May/98 (Type2) ;# Created on: 29/Mar/99 (Type3) ;# I can be reached at: resq@rescue.ne.jp ;# Scripts Found at: http://www.rescue.ne.jp/ #-------------------------------------------------------------------------------------- # < >は設定が必要なパーミッション値 # # |--/data/ <777> # | | # | |- index3 (DBM)(*1) # | |- minibbs3.txt (バックアップファイル)(*3) # | # |- cgi-lib217.pl <644> # |- jcode.pl <644> # |- minibbs.cgi (このプログラム) <755> # |- password.cgi (パスワードファイル)(*2) <666> # # (*1)..拡張子は設置されたサーバ環境によって異なる. 拡張子がない場合もある. # 自動的に作成されるので用意する必要はなく、直接いじることはできない. # (*2)..この拡張子は単にCGIとして認識させてウェブ上から見えなくするためのもの. # 運用初回の場合、空のファイルを用意する. # (*3)..用意しなくても自動作成される. 手を加えずそのまま保存するものとする. # 技術の無い方のこのファイルの編集を勧めないし、サポートはしない. # あくまでもバックアップのためのファイルである. #-------------------------------------------------------------------------------------- # [履歴] # # v1.00 29/Mar/99 初版 # v1.10 29/Mar/99 perl4およびGDBM_File以外のモジュール対応(自動判定) # v1.11 30/Mar/99 タイトルバーが表示されないバグの修正 # v1.12 30/Mar/99 NN3でJavaScriptエラーになる不具合を修正 # ----- (ここを境にデータ互換はありません) # v2.00 31/Mar/99 本文とインデックスを分離管理 # v2.01 03/Apr/99 別フレーム表示の不具合の修正 # v2.02 08/Apr/99 バックアップ処理のバグの修正 # v2.02a 22/Apr/99 保存記事数制限(設定件数を超えたら最古記事を1件削除) # v2.03a 13/Sep/00 検索処理のバグの修正($string関係) # v2.04a 14/Sep/00 検索処理のバグの修正の修正 # v2.05a 11/Nov/03 暗号化処理の修正 #----------------# # 初期設定 # #----------------# #--- あなたの環境に合わせて必ず書き替える項目 --------------------------------------------# #◆このスクリプトをURLで設定 $reload = 'http://設置したURL/minibbs.cgi'; #◆画面の「終了」リンク先をURLで設定 $modoru = 'http://ホームページなどのURL/'; #--- 必要に応じて設定する項目 ------------------------------------------------------------# #◆掲示板の名称(タグ有効) # ''内に記述しますが、'を入れたい場合は '' を "" に替えて(文字化けに注意)ください. $title = '

簡易BBS

'; #◆ブラウザのタイトルバーの名称 # ''内に記述しますが、'を入れたい場合は '' を "" に替えて(文字化けに注意)ください. $title_bar = '簡易BBS'; #◆画面の色や背景の設定 (HTML書式) $body = ''; #◆記事題名の文字色 $subject_color = '#ffeedd'; #◆記事ヘッダ(名前や投稿日など)の文字色 $head_color = '#ffff00'; #◆記事内容の文字色 $body_color = '#ffffff'; #◆1画面に表示する本文の件数(デフォルト)..コメントは含まれない $def = 10; #◆保存記事数上限 # (既にこれ以上の記事が存在している場合は正常に機能しません.) $full = 100; #◆ホスト名の表示の可否 1:する 0:しない $view_host = 1; #◆解説文の挿入 1:する 0:しない $info = 1; #◆タイトルの下位置に表示する文字列 # $msg_top = <<'EOF'; # この間に記述すること # EOF $msg_top = <<'EOF'; EOF #◆新規投稿フォーム前表示する文字列 $msg_btm = <<'EOF'; EOF #◆$reloadで設定した設置URL以外のフォームからの投稿を禁止する処置 する:1 しない:0 # 悪戯の防止用ですが、利用環境によっては正常投稿もできなくなる場合もあります. $ref_axs = 0; #◆ 記録モードのデフォルトチェック値 # 0:改行無効 1:図表モード 2:改行有効 $chk = 0; #◆時差調整 # +9時間する場合 = localtime(time + 9*60*60); # −9時間する場合 = localtime(time - 9*60*60); # または、$ENV{'TZ'} = "Japan"; をlocaltime()の前に記述する. ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time); #◆CGIライブラリ(Path) require './cgi-lib217.pl'; require './jcode.pl'; #◆データディレクトリ(Path) $data_dir = './data/'; #◆パスワードファイル(Path) $pwd_file = './password.cgi'; #◆DBM名(拡張子は付けない)...この名称と同じファイル名を他につけないこと. $dbm_name = 'index3'; #◆バックアップファイル名 $seq_name = 'minibbs3.txt'; #-----------------------------------------------------------------------------------------# &check_version; $DBM = $data_dir . $dbm_name; $wcheck = $data_dir . 'wcheck.txt'; $lockfile = $data_dir . 'minibbs3.lock'; &lock; umask(000); if ($pl == 4) { if (!dbmopen(%DAT,$DBM,0666)) { &error('エラー',"$dbm_typeが開けませんでした."); } } else { if ($dbm_type eq 'GDBM_File') { eval 'use GDBM_File;'; } elsif ($dbm_type eq 'SDBM_File') { eval 'use SDBM_File;'; } elsif ($dbm_type eq 'ODBM_File') { eval 'use ODBM_File;'; } eval 'use Fcntl;'; eval 'tie %DAT,$dbm_type,$DBM,O_CREAT|O_RDWR,0666;'; if ($@ ne '') { &error('エラー',"$dbm_typeが開けませんでした.",$@); } } &decode_cookie($reload); $cmd = $ENV{'QUERY_STRING'}; @pairs = split(/&/,$cmd); foreach $pair (@pairs) { ($name,$value) = split(/=/,$pair); $value =~ tr/+/ /; $value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg; $CMD{$name} = $value; } if ($CMD{'action'} eq 'copyright') { ©right; exit; } elsif ($CMD{'action'} eq 'new') { &new; exit; } elsif ($CMD{'action'} eq 'backup') { &backup; exit; } elsif ($CMD{'action'} eq 'backupf') { &backupf; exit; } elsif ($CMD{'action'} eq 'restore') { &restore; exit; } elsif ($CMD{'action'} eq 'restoref') { &restoref; exit; } elsif ($CMD{'view'} =~ /(\d+)/) { &point_view($1); exit; } elsif ($CMD{'action'} ne "") { &error("不正処理の検出"); } if ($CMD{'page'} ne '') { $in{'page'} = $CMD{'page'}; } if ($chk < 0 || $chk > 2) { $chk = 0; } $howc[$chk] = "checked"; &ReadParse; if ($in{'action'} eq 'setpwd') { &setpwd; } if (!-e $pwd_file) { &error("エラー","管理者用パスワードファイルが見つかりません."); } if (-z $pwd_file || $in{'action'} eq 'resetpwd') { &setpwd_form; } while (($key,$val) = each %in) { &jcode'convert(*val,'sjis'); $val =~ s/&/&/g; $val =~ s/"/"/g; $val =~ s//>/g; $val =~ s/\t//g; $val =~ s/\r\n/\r/g; $val =~ s/\n/\r/g; if ($key =~ /res:(\d+)&page:(\d+)/) { &resform($1,$2); } elsif ($key =~ /rep:(\d+)&page:(\d+)/) { &repform($1,$2); } else { $in{$key} = $val; } } @wday_array = ('日','月','火','水','木','金','土'); $date_now = sprintf("%04d/%01d/%01d(%s) %02d:%02d",$year +1900,$mon +1,$mday,$wday_array[$wday],$hour,$min); $days_now = &Days($year +1900,$mon +1,$mday); if ($DAT{'number'} eq '') { $DAT{'number'} = 1; } if ($DAT{'version'} eq '') { $DAT{'version'} = 'MiniBBS 3 v2'; } elsif ($DAT{'version'} ne 'MiniBBS 3 v2') { &error('エラー','インデックスファイル(DBM)のバージョンが違います.'); } if ($in{'target'} ne '' && $in{'action'} eq 'remove') { &remove; } elsif ($in{'action'} eq 'regist') { ®ist; } elsif ($in{'action'} eq 'rep_regist') { &rep_regist; } $i = $DAT{'number'} - 1; if ($in{'page'} =~ /\D/ || $in{'list'} =~ /\D/) { &error('入力ミス',"半角数字で入力してください."); } if ($in{'list'} ne '') { $def = $in{'list'}; } if ($in{'page'} == 0) { $page = $i; } else { $page = $in{'page'}; } if ($page < 0 || $page > $i) { &error('指定範囲外',"$iまでの番号を指定してください."); } if ($in{'search'} ne '') { &search; } else { &pickup; } $NUMBER = $DAT{'number'} - 1; &html; if ($pl == 4) { dbmclose(%DAT); } else { eval 'untie %DAT;'; } if (-e $lockfile) { unlink $lockfile; } exit; sub search { if ($in{'search'} =~ /[&"<>]/) { &error("入力文字制限","検索文字列に記号の入力はできません."); } $keys = $target = $in{'search'}; $keys =~ s/ / /g; $target =~ s/ / /g; $target =~ s/(\W)/\\$1/g; @keys = split(/\\\s+/,$target); $next_num = ''; $hit = 0; foreach $key (reverse 1 .. $page) { if ($DAT{$key} eq '') { next; } $string = ""; ($pwd,$date,$name,$email,$host,$subject,$how,$link,$base,$res) = split(/\t/,$DAT{$key}); if ($base ne '') { next; } if ($res ne '') { foreach $r (split(/\,/,$res)) { { local($pwd,$date,$name,$email,$host,$subject,$how,$link,$base,$res); ($pwd,$date,$name,$email,$host,$subject,$how,$link,$base,$res) = split(/\t/,$DAT{$r}); $string .= "$name $email $subject "; } } } $string .= "$name $email $subject "; if ($in{'mode'} eq 'or') { $match = 1; foreach $term (@keys) { if ($string =~ /$term/i) { $match = 0; }} } else { $match = 0; foreach $term (@keys) { if (!($string =~ /$term/i)) { $match = 1; }} } if ($match) { next; } if ($hit == $def) { $next_num = $key; last; } else { push(@PICKUP,"$key\t$DAT{$key}"); $hit++; } } } sub pickup { $next_num = ''; $hit = 0; foreach $key (reverse 1 .. $page) { if ($DAT{$key} eq '') { next; } $string = $DAT{$key}; ($pwd,$date,$name,$email,$host,$subject,$how,$link,$base,$res) = split(/\t/,$string); if ($base ne '') { next; } if ($hit == $def) { $next_num = $key; last; } else { push(@PICKUP,"$key\t$DAT{$key}"); $hit++; } } } sub html { if ($in{'mode'} eq 'or') { $OR = 'checked'; $MODE = ' または '; } elsif ($in{'mode'} eq 'and' || $in{'mode'} eq '') { $AND = 'checked'; $MODE = ' かつ '; } unless ($keys =~ / /) { $MODE = ''; } $keys2 = $keys; $keys2 =~ s/ /$MODE/g; if ($COOKIE{'uname'} eq '') { $focus = 'onClick="document.InputForm.uname.focus();"'; } else { $focus = 'onClick="document.InputForm.subject.focus();"'; } if ($in{'cook'} eq '') { $c = $reload . '2'; &decode_cookie($c); } else { $COOKIE{'RC'} = $in{'cook'}; } if ($in{'cook'} eq '') { print "Set-Cookie: $reload" . '2' . "=RC:$NUMBER; expires=$date_gmt\n"; } print &PrintHeader; print <<"EOF"; $title_bar $body $title $msg_top

EOF if ($in{'search'} ne '' || @PICKUP) { print <<"EOF"; EOF } print <<"EOF";
文字列 AND OR [新規投稿] [更新] [終了]
EOF if ($info) { print '※文字列はスペースで区切って複数指定できます. ※投稿者,Eメール,題名からのみ検索します.'; } print "

\n"; if ($COOKIE{'RC'} ne '' && $COOKIE{'RC'} < $NUMBER && $in{'search'} eq '') { print <<"EOF";


【新着案内】New!

\n"; } if ($in{'search'} ne '') { print <<"EOF";


《検索モード》

検索条件 → $keys2

EOF } if (!@PICKUP) { if ($in{'search'} ne '') { print "抽出されたデータはありません.

\n"; } else { print "


このページにデータはありません.

\n"; } } else { ($review,$pwd,$date,$name,$email,$host,$subject,$how,$link,$base,$res) = split(/\t/,$PICKUP[0]); if ($def > $#PICKUP + 1) { $def = $#PICKUP + 1; } print "

\n"; print "\n"; print "\n"; print "\n"; print "\n"; print "\n"; foreach (@PICKUP) { ($number,$pwd,$date,$name,$email,$host,$subject,$how,$link,$base,$res) = split(/\t/); if (open(IN,"$data_dir$number\.txt")) { @lines = ; close(IN); $value = join ('',@lines); } else { $value = '《データ(本文)が見つからないか読み出せません》'; } $value =~ s/"/\"/g; $value =~ s/&/\&/g; if ($link == 1) { $value =~ s/(https?|ftp|gopher|telnet|whois|news)\:([\w|\:\!\#\$\%\=\&\-\^\`\\\|\@\~\[\{\]\}\;\+\*\,\.\?\/]+)/$1\:$2<\/a>/ig; } if ($COOKIE{'RC'} ne '' && $number > $COOKIE{'RC'}) { $new = "\"New!\""; } else { $new = ""; } print "
$new $subject
\n"; if ($view_host && $host ne '') { $host = "[$host]"; } else { $host = ""; } if ($email ne '') { print "No.$number by $name $host"; } else { print "No.$number by $name $host"; } if ($res ne '') { $delwarn = '(コメントを含む)'; } else { $delwarn = ''; } print " at $date "; print " "; print " 削除$delwarn

\n"; print "\n"; if ($how == 1) { print "

$value

\n"; } elsif ($how == 2) { $value =~ s/\r/
\r/g; print "$value

\n"; } else { print "$value

\n"; } print "\n"; if ($res ne '') { print "

\n"; foreach $r (split(/\,/,$res)) { ($pwd,$date,$name,$email,$host,$subject,$how,$link,$base,$res) = split(/\t/,$DAT{$r}); if (open(IN,"$data_dir$r\.txt")) { @lines = ; close(IN); $value = join ('',@lines); } else { $value = '《データ(本文)が見つからないか読み出せません》'; } $value =~ s/"/\"/g; $value =~ s/&/\&/g; if ($link == 1) { $value =~ s/(https?|ftp|gopher|telnet|whois|news)\:([\w|\:\!\#\$\%\=\&\-\^\`\\\|\@\~\[\{\]\}\;\+\*\,\.\?\/]+)/$1\:$2<\/a>/ig; } if ($COOKIE{'RC'} ne '' && $r > $COOKIE{'RC'}) { $new = "\"New!\""; } else { $new = ""; } print "
$new\n"; if ($view_host && $host ne '') { $host = "[$host]"; } else { $host = ""; } if ($email ne '') { print "No.$r by $name $host"; } else { print "No.$r by $name $host"; } print " at $date "; print " 削除

\n"; if ($how == 1) { print "

$value

\n"; } elsif ($how == 2) { $value =~ s/\r/
\r/g; print "$value

\n"; } else { print "$value

\n"; } } print "

\n"; } } } print "
\n"; print "

\n"; if (@PICKUP) { print "\n"; } if ($next_num ne '') { print "\n"; print "\n"; print "\n"; print "\n"; print "\n"; print "\n"; print "\n"; } print <<"EOF";
削除用パスワード "; print "
[更新] [終了]
EOF if (@PICKUP && $info) { print <<"EOF"; ※あなたが投稿した記事を削除するには、削除 をチェックして削除用パスワードを入力してを押します.
※返信(コメント)のある記事を削除すると、コメントも全て削除されます.
EOF } print <<"EOF";

番号による一覧 番付近から
EOF if ($info) { print '※番号による一覧は新規投稿データのみが対象となります.'; } print <<"EOF";


↓新規投稿

$msg_btm

投稿者
Eメール
題名
内容 改行無効 改行有効 図/表\モード

URLをリンクする
この記事の削除用パスワード (6文字以上の文字列) 投稿者とメールとパスワードを保存

MiniBBS-3 v2.02a
EOF print &HtmlBot; } sub resform { local($base,$page) = @_; if ($DAT{$base} eq '') { &error("見つかりません","No.$baseのデータが見つかりません."); } if ($body =~ /$title_bar $body

No.$baseへのコメント

投稿者
Eメール
内容 改行無効 改行有効 図/表\モード

URLをリンクする
この記事の削除用パスワード (6文字以上の文字列) 投稿者とメールとパスワードを保存

[戻る]

EOF if (-e $lockfile) { unlink $lockfile; } if ($pl == 4) { dbmclose(%DAT); } else { eval 'untie %DAT;'; } exit; } sub repform { local($rep,$review) = @_; if ($DAT{$rep} eq '') { &error("見つかりません","No.$repのデータが見つかりません."); } ($pwd,$date,$name,$email,$host,$subject,$how,$link,$base,$res) = split(/\t/,$DAT{$rep}); if (open(IN,"$data_dir$rep\.txt")) { @lines = ; close(IN); $value = join ('',@lines); } else { $value = ''; } $howcr[$how] = "checked"; if ($link) { $linkc = "checked"; } print &PrintHeader; print <<"EOF"; $title_bar $body

No.$repの再編集

EOF if ($base eq '') { print <<"EOF"; EOF } print <<"EOF";
投稿者
Eメール
題名
内容 改行無効 改行有効 図/表\モード

URLをリンクする
削除用パスワード 投稿者とメールとパスワードを保存

※最初に投稿した時の削除用パスワードの入力が必要です.

[戻る]

EOF if (-e $lockfile) { unlink $lockfile; } if ($pl == 4) { dbmclose(%DAT); } else { eval 'untie %DAT;'; } exit; } sub regist { if ($ref_axs) { $ref = $ENV{'HTTP_REFERER'}; $ref =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C",hex($1))/eg; if (!($ref =~ /$reload/)) { &error('利用不可',"次のページ以外からの投稿は受け付けられません.
→ $reload"); } } if ($in{'uname'} eq '') { &error('入力ミス','投稿者を記入してください.'); } if ($in{'uname'} =~ /\;/) { &error('入力ミス','投稿者にセミコロンは使えません.'); } if ($in{'uname'} =~ /\,/) { &error('入力ミス','投稿者にカンマは使えません.'); } if (length($in{'uname'}) > 50) { &error('入力ミス','投稿者名を50バイト(全角なら25文字)以内でご記入ください.'); } if ($in{'email'} ne '' && !($in{'email'} =~ /\b[-\w.]+@[-\w.]+\.[-\w]+\b/)) { &error('入力ミス','Eメールの形式が間違っています.'); } if (length($in{'email'}) > 100) { &error('入力ミス','100文字を超えるEメールアドレスの入力はできません.'); } if ($in{'base'} eq '' && $in{'subject'} eq '') { &error('入力ミス','題名を記入してください.'); } if (length($in{'subject'}) > 50) { &error('入力ミス','題名を50バイト(全角なら25文字)以内でご記入ください.'); } if ($in{'value'} eq '') { &error('入力ミス','内容を記入してください.'); } if ($in{'pwd'} eq '' || length($in{'pwd'}) < 6) { &error('入力ミス','削除キー欄に6文字以上の半角文字でパスワードを指定してください.
これは記事を削除する時に利用するものです.'); } if (length($in{'pwd'}) > 20) { &error('入力ミス','削除キーは20文字以内でご記入ください.'); } $host = $ENV{'REMOTE_HOST'}; $addr = $ENV{'REMOTE_ADDR'}; if ($host eq '') { $host = $addr; } if ($host eq $addr) { $host = gethostbyaddr(pack('C4',split(/\./,$host)),2) || $addr; } $MSG = "$in{'uname'}\t$in{'email'}\t$host\t$in{'subject'}\t$in{'how'}\t$in{'link'}\t$in{'base'}\t$in{'res'}"; $MSGc = "$in{'uname'}\t$in{'email'}\t$in{'subject'}\t$in{'value'}"; if (!-e $wcheck) { if (!open(CHECK,"> $wcheck")) { &error("エラー","チェックファイルが作成できません."); } close(CHECK); } if (open(CHECK,$wcheck)) { $cmsg = ; close(CHECK); if ($cmsg eq $MSGc) { return; } else { if (!open(CHECK,"> $wcheck")) { return; } print CHECK $MSGc; close(CHECK); chmod(0666,$wcheck); } } if ($in{'base'} ne '') { if ($DAT{$in{'base'}} eq '') { &error("エラー","コメントするデータが見つかりませんでした.\n
削除された可能\性があります."); } { local($pwd,$date,$name,$email,$host,$subject,$how,$link,$base,$res); ($pwd,$date,$name,$email,$host,$subject,$how,$link,$base,$res) = split(/\t/,$DAT{$in{'base'}}); if ($base ne '') { &error("エラー","レスポンスデータにコメント(レス)を付けることはできません."); } @RES = split(/\,/,$res); push(@RES,$DAT{'number'}); $res = join (',',@RES); $DAT{$in{'base'}} = "$pwd\t$date\t$name\t$email\t$host\t$subject\t$how\t$link\t$base\t$res"; } } ($pwd) = &MakeCrypt($in{'pwd'}); if ($pwd eq '') { &error("エラー","暗号化に失敗しました."); } if ($in{'cookie'} eq 'on') { $cook="uname\:$in{'uname'}\,email\:$in{'email'}\,pwd\:$in{'pwd'}"; print "Set-Cookie: $reload=$cook; expires=$date_gmt\n"; $COOKIE{'uname'} = $in{'uname'}; $COOKIE{'email'} = $in{'email'}; $COOKIE{'pwd'} = $in{'pwd'}; } else { print "Set-Cookie: $reload=\n"; $COOKIE{'uname'} = $COOKIE{'email'} = $COOKIE{'pwd'} = ''; } if (!open(OUT,"> $data_dir$DAT{'number'}\.txt")) { &error("エラー","データが記録できません."); } print OUT $in{'value'}; close(OUT); chmod(0666,"$data_dir$DAT{'number'}\.txt"); &limit; $DAT{$DAT{'number'}} = "$pwd\t$date_now\t$MSG"; $DAT{'number'}++; } sub rep_regist { if ($ref_axs) { $ref = $ENV{'HTTP_REFERER'}; $ref =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C",hex($1))/eg; if (!($ref =~ /$reload/)) { &error('利用不可',"次のページ以外からの投稿は受け付けられません.
→ $reload"); } } if ($in{'uname'} eq '') { &error('入力ミス','投稿者を記入してください.'); } if ($in{'uname'} =~ /\;/) { &error('入力ミス','投稿者にセミコロンは使えません.'); } if ($in{'uname'} =~ /\,/) { &error('入力ミス','投稿者にカンマは使えません.'); } if (length($in{'uname'}) > 50) { &error('入力ミス','投稿者名を50バイト(全角なら25文字)以内でご記入ください.'); } if ($in{'email'} ne '' && !($in{'email'} =~ /\b[-\w.]+@[-\w.]+\.[-\w]+\b/)) { &error('入力ミス','Eメールの形式が間違っています.'); } if (length($in{'email'}) > 100) { &error('入力ミス','100文字を超えるEメールアドレスの入力はできません.'); } if ($in{'value'} eq '') { &error('入力ミス','内容を記入してください.'); } if ($in{'pwd'} eq '' || length($in{'pwd'}) < 6) { &error('入力ミス','最初に投稿した時の削除用パスワードを削除キー欄に入力してください.'); } if (length($in{'pwd'}) > 20) { &error('入力ミス','削除キーは20文字以内でご記入ください.'); } $MSGc = "$in{'uname'}\t$in{'email'}\t$in{'subject'}\t$in{'value'}"; if (open(CHECK,$wcheck)) { $cmsg = ; close(CHECK); if ($cmsg eq $MSGc) { return; } else { if (!open(CHECK,"> $wcheck")) { return; } print CHECK $MSGc; close(CHECK); chmod(0666,$wcheck); } } if ($DAT{$in{'base'}} eq '') { &error("エラー","修正するデータNo.$in{'base'}が見つかりませんでした.\n
削除された可能\性があります."); } ($pwd,$date,$name,$email,$host,$subject,$how,$link,$base,$res) = split(/\t/,$DAT{$in{'base'}}); if ($base eq '' && $in{'subject'} eq '') { &error('入力ミス','題名を記入してください.'); } ($admin) = &master_check; if ($pwd =~ /^\$1\$/) { $salt = 3; } else { $salt = 0; } if (!$admin && crypt($in{'pwd'},substr($pwd,$salt,2)) ne $pwd) { &error("修正できません","最初に投稿した時の削除用パスワードが合いません."); } if ($base ne '' && $DAT{$base} eq '') { &error("エラー","コメントするデータが見つかりませんでした.\n
削除された可能\性があります."); } $MSG = "$in{'uname'}\t$in{'email'}\t$host\t$in{'subject'}\t$in{'how'}\t$in{'link'}\t$base\t$res"; if ($in{'cookie'} eq 'on') { $cook="uname\:$in{'uname'}\,email\:$in{'email'}\,pwd\:$in{'pwd'}"; print "Set-Cookie: $reload=$cook; expires=$date_gmt\n"; $COOKIE{'uname'} = $in{'uname'}; $COOKIE{'email'} = $in{'email'}; $COOKIE{'pwd'} = $in{'pwd'}; } else { print "Set-Cookie: $reload=\n"; $COOKIE{'uname'} = $COOKIE{'email'} = $COOKIE{'pwd'} = ''; } if (!open(OUT,"> $data_dir$in{'base'}\.txt")) { &error("エラー","データが記録できません."); } print OUT $in{'value'}; close(OUT); chmod(0666,"$data_dir$in{'base'}\.txt"); $DAT{$in{'base'}} = "$pwd\t$date_now\t$MSG"; } sub remove { ($admin) = &master_check; foreach $d (split("\0",$in{'target'})) { if ($DAT{$d} eq '') { next; } ($pwd,$date,$name,$email,$host,$subject,$how,$link,$base,$res) = split(/\t/,$DAT{$d}); if ($pwd =~ /^\$1\$/) { $salt = 3; } else { $salt = 0; } if ((!$admin) && crypt($in{'pwd'},substr($pwd,$salt,2)) ne $pwd) { push(@ERR,"No.$dのパスワードが合わないので削除できませんでした."); next; } if ($base eq '' && $res eq '') { delete $DAT{$d}; unlink "$data_dir$d\.txt"; } elsif ($res ne '') { delete $DAT{$d}; unlink "$data_dir$d\.txt"; foreach $r (split(/\,/,$res)) { delete $DAT{$r}; unlink "$data_dir$r\.txt"; } } elsif ($base ne '') { delete $DAT{$d}; unlink "$data_dir$d\.txt"; $tbase = $base; { local($pwd,$date,$name,$email,$host,$subject,$how,$link,$base,$res); ($pwd,$date,$name,$email,$host,$subject,$how,$link,$base,$res) = split(/\t/,$DAT{$tbase}); @RES = split(/\,/,$res); @RES = grep(!/$d/,@RES); $res = join (',',@RES); $DAT{$tbase} = "$pwd\t$date\t$name\t$email\t$host\t$subject\t$how\t$link\t$base\t$res"; } } else { delete $DAT{$d}; unlink "$data_dir$d\.txt"; } } if (@ERR) { &error("削除できなかったデータの報告",@ERR); } } sub master_check { local($admin); if (!open(READ,$pwd_file)) { &error('エラー','管理者用パスワードファイルが読み出せません.'); } $master = ; close(READ); chop($master) if $master =~ /\n/; if ($master ne '') { if ($master =~ /^\$1\$/) { $salt = 3; } else { $salt = 0; } if (crypt($in{'pwd'},substr($master,$salt,2)) eq $master) { $admin = 1; } else { $admin = 0; } } else { $admin = 0; } return $admin; } sub point_view { local($num) = @_; if ($DAT{$num} eq '') { &error("見つかりません","No.$numは削除されている可能\性があります."); } ($pwd,$date,$name,$email,$host,$subject,$how,$link,$base,$res) = split(/\t/,$DAT{$num}); if ($base ne '') { &error("エラー","番号指定が不正です.
\n直接コメントを指定することはできません."); } if (!open(IN,"$data_dir$num\.txt")) { &error("Not Found","No.$numのデータ(本文)が見つからないか読み出せません."); } @lines = ; close(IN); $value = join ('',@lines); print &PrintHeader; print <<"EOF"; $title_bar $body EOF $value =~ s/"/\"/g; $value =~ s/&/\&/g; if ($link == 1) { $value =~ s/(https?|ftp|gopher|telnet|whois|news)\:([\w|\:\!\#\$\%\=\&\-\^\`\\\|\@\~\[\{\]\}\;\+\*\,\.\?\/]+)/$1\:$2<\/a>/ig; } print "$subject
\n"; if ($view_host && $host ne '') { $host = "[$host]"; } else { $host = ""; } if ($email ne '') { print "No.$num by
$name $host"; } else { print "No.$num by $name $host"; } print " at $date

\n"; print "\n"; if ($how == 1) { print "

$value

\n"; } elsif ($how == 2) { $value =~ s/\r/
\r/g; print "$value

\n"; } else { print "$value

\n"; } print "\n"; if ($res ne '') { print "

\n"; foreach $r (split(/\,/,$res)) { ($pwd,$date,$name,$email,$host,$subject,$how,$link,$base,$res) = split(/\t/,$DAT{$r}); if (open(IN,"$data_dir$r\.txt")) { @lines = ; close(IN); $value = join ('',@lines); } else { $value = '《データ(本文)が見つからないか読み出せません》'; } $value =~ s/"/\"/g; $value =~ s/&/\&/g; if ($link == 1) { $value =~ s/(https?|ftp|gopher|telnet|whois|news)\:([\w|\:\!\#\$\%\=\&\-\^\`\\\|\@\~\[\{\]\}\;\+\*\,\.\?\/]+)/$1\:$2<\/a>/ig; } print "
\n"; if ($view_host && $host ne '') { $host = "[$host]"; } else { $host = ""; } if ($email ne '') { print "No.$r by
$name $host"; } else { print "No.$r by $name $host"; } print " at $date

\n"; if ($how == 1) { print "

$value

\n"; } elsif ($how == 2) { $value =~ s/\r/
\r/g; print "$value

\n"; } else { print "$value

\n"; } } print "

\n"; } print &HtmlBot; if (-e $lockfile) { unlink $lockfile; } if ($pl == 4) { dbmclose(%DAT); } else { eval 'untie %DAT;'; } exit; } sub decode_cookie { #---------------------------------------------------------------- # 関数 クッキーデータの抽出/出力フォーマット # 引数 クッキー名 # 戻値 なし (%COOKIE) #---------------------------------------------------------------- local($name) = @_; local($cookies); ($secg,$ming,$hourg,$mdayg,$mong,$yearg,$wdayg,$ydayg,$isdstg) = gmtime(time + 30*24*60*60); $y0="Sunday"; $y1="Monday"; $y2="Tuesday"; $y3="Wednesday"; $y4="Thursday"; $y5="Friday"; $y6="Saturday"; @youbi = ($y0,$y1,$y2,$y3,$y4,$y5,$y6); $m0="Jan"; $m1="Feb"; $m2="Mar"; $m3="Apr"; $m4="May"; $m5="Jun"; $m6="Jul"; $m7="Aug"; $m8="Sep"; $m9="Oct"; $m10="Nov"; $m11="Dec"; @monthg = ($m0,$m1,$m2,$m3,$m4,$m5,$m6,$m7,$m8,$m9,$m10,$m11); $date_gmt = sprintf("%s\, %02d\-%s\-%04d %02d:%02d:%02d GMT",$youbi[$wdayg],$mdayg,$monthg[$mong],$yearg +1900,$hourg,$ming,$secg); $cookies = $ENV{'HTTP_COOKIE'}; @pairs = split(/;/,$cookies); foreach $pair (@pairs) { ($key,$val) = split(/=/,$pair,2); $key =~ s/ //g; if ($key eq $name) { @pairs = split(/,/,$val); foreach $pair (@pairs) { ($key,$val) = split(/:/,$pair,2); $COOKIE{$key} = $val; } last; } } } sub setpwd_form { print &PrintHeader; print <<"EOF"; $title_bar $body

管理者パスワードの設定/変更

EOF if (!-z $pwd_file) { print "現パスワード
\n"; } print <<"EOF"; 新パスワード
新パスワード (もう一度)


[戻る]

[インデックスデータのバックアップ]
[バックアップデータのリストア]


動作中のDBMの種類:$dbm_type

EOF print &HtmlBot; if (-e $lockfile) { unlink $lockfile; } exit; } sub setpwd { if (!-z $pwd_file) { if (!open(READ,$pwd_file)) { &error('エラー','管理者用パスワードファイルが読み出せません.'); } $master = ; close(READ); chop($master) if $master =~ /\n/; if ($master =~ /^\$1\$/) { $salt = 3; } else { $salt = 0; } if (crypt($in{'old_password'},substr($master,$salt,2)) ne $master) { &error("Authorization Required",'現パスワードが認証されませんでした.'); } } if (length($in{'new_password'}) < 6 || $in{'new_password'} eq '') { &error('入力ミス','6文字以上のパスワードを指定してください.'); } if ($in{'new_password'} ne $in{'retype_password'}) { &error('入力ミス','2回入力したパスワードが合いません.'); } ($pwd) = &MakeCrypt($in{'new_password'}); if (!open(WRITE,"> $pwd_file")) { &error('エラー','管理者用パスワードファイルに記録できません.'); } print WRITE $pwd; close(WRITE); } sub MakeCrypt { #---------------------------------------------------------------- # 関数 文字列の暗号化 # 引数 平文 # 戻値 暗号文 #---------------------------------------------------------------- local($plain) = @_; my $mypass = $_[0]; srand(); my @saltset = ('a'..'z','A'..'Z','0'..'9'); my $nsalt = $saltset[int(rand(64))] . $saltset[int(rand(64))]; $mypass = crypt($mypass,$nsalt); $mypass; } sub Days { #---------------------------------------------------------------- # 関数 日数計算 # 引数 西暦,月,日 # 戻値 日数 #---------------------------------------------------------------- local ($year,$month,$day) = @_; local ($base,$i,$y); $days[4] = $days[6] = $days[9] = $days[11] = 30; $days[1] = $days[3] = $days[5] = $days[7] = $days[8] = $days[10] = $days[12] = 31; $days[2] = 28; unless ($year % 4) { $days[2] = 29; } unless ($year % 100) { $days[2] = 28; } unless ($year % 400) { $days[2] = 29; } $y = $year - 1; $base = ($y * 365) + ($y / 4) - ($y / 100) + ($y / 400); $i = $month; if ($i == 0) { &error("Error"); } while ( --$i ) { $base += $days[$i]; } return int($base + $day); } sub lock { $symlink_check = (eval { symlink("",""); }, $@ eq ""); if (!$symlink_check) { &lock2; } else { &lock1; } } sub lock1 { local($retry) = 3; while (!symlink(".", $lockfile)) { if (--$retry <= 0) { &error('リトライエラー','ただいま混雑しております.
もう一度実行してみてください.'); } sleep(2); } } sub lock2 { $c = 0; while(-f "$lockfile") { $c++; if ($c >= 3) { &error('リトライエラー','ただいま混雑しております.
もう一度実行してみてください.'); } sleep(2); } open(LOCK,">$lockfile"); close(LOCK); } sub check_version { local($a,$b); if ($] > 5) { $a = 'use GDBM_File;'; $dbm_type = "GDBM_File"; eval $a; if ($@ ne '') { $a = 'use SDBM_File;'; $dbm_type = "SDBM_File"; eval $a; if ($@ ne '') { $a = 'use ODBM_File;'; $dbm_type = "ODBM_File"; eval $a; if ($@ ne '') { $dbm_type = 'DBM for perl4'; $pl = 4; } } } $b = 'use Fcntl;'; eval $b; if ($@ ne '') { $dbm_type = 'DBM for perl4'; $pl = 4; } } else { $dbm_type = 'DBM for perl4'; $pl = 4; } } sub backupf { if (-e $lockfile) { unlink $lockfile; } print "Content-type: text/html\n\n"; print <<"EOF"; Utility $body

バックアップ(兼 更新)

  • インデックスファイル(DBM)をテキスト形式のバックアップファイルに変換します。
  • 本体(記事内容が記録されたファイル)はバックアップファイルに記録されません。
  • 本体が存在していないインデックスデータはバックアップファイルに記録されません。
  • コメント元(本体)が削除されたコメントはバックアップファイルに記録されません。

EOF print <<"EOF";

管理者パスワード (データ量に応じて時間がかかるので1回だけ押して待つこと)
EOF if ($pl == 4) { dbmclose(%DAT); } else { eval 'untie %DAT;'; } } sub backup { $SEQ = $data_dir . $seq_name; ($ret) = &ReadParse; if ($ret == 0) { &backupf; } ($admin) = &master_check; if (!$admin) { &error('Authorization Required','パスワードが認証されませんでした.'); } $tstart = time; $start = (times)[0]; if ($DAT{'version'} ne 'MiniBBS 3 v2') { &error('エラー','インデックスファイル(DBM)のバージョンが違います.'); } if (!open(SEQ,"> $SEQ")) { &error('SEQ Write Error','バックアップファイルに記録できません.'); } print SEQ "# MiniBBS-3 DBM Backup File\n"; print SEQ "# This is a generated file! Do not edit.\n\n"; while (($key,$val) = each %DAT) { if ($key ne 'number') { if (!-e "$data_dir$key\.txt") { next; } ($pwd,$date,$name,$email,$host,$subject,$how,$link,$base,$res) = split(/\t/,$DAT{$key}); if ($base ne '' && !-e "$data_dir$base\.txt") { next; } if ($res ne '') { @RES = (); foreach $r (split(/\,/,$res)) { if (!-e "$data_dir$r\.txt") { next; } push(@RES,$r); $res = join (',',@RES); $val = "$pwd\t$date\t$name\t$email\t$host\t$subject\t$how\t$link\t$base\t$res"; } } } print SEQ "$key\t$val\n"; } close(SEQ); chmod(0666,$SEQ); if ($pl == 4) { dbmclose(%DAT); } else { eval 'untie %DAT;'; } if (-e $lockfile) { unlink $lockfile; } $tend = time; $end = (times)[0]; print "Content-type: text/html\n\n"; print <<"EOF"; Utility $body

バックアップ終了

  • DBMからバックアップファイル(minibbs3.txt)が作成されました。
  • テキストファイルですので、取り出してパソ\コン等に保存してください。
  • 記事本文はこのファイルに含まれません。
  • バックアップファイルとその他の$data_dir内の記事本文「記事番号.txt」は同時に保存してください。

EOF printf "ACCESSTIME: %.3f seconds.
\n", $tend - $tstart; printf "CPUTIME: %.3f CPU seconds.

\n", $end - $start; print "[戻る]\n"; print "\n"; } sub restoref { if (-e $lockfile) { unlink $lockfile; } print "Content-type: text/html\n\n"; print <<"EOF"; Utility $body

リストア

  • バックアップファイルからインデックスデータ(DBM)のみを再生します。
  • 現在のインデックスデータは削除されるので注意してください。

管理者パスワード (データ量に応じて時間がかかるので1回だけ押して待つこと)
EOF if ($pl == 4) { dbmclose(%DAT); } else { eval 'untie %DAT;'; } } sub restore { $SEQ = $data_dir . $seq_name; ($ret) = &ReadParse; if ($ret == 0) { &restoref; } ($admin) = &master_check; if (!$admin) { &error('Authorization Required','パスワードが認証されませんでした.'); } $tstart = time; $start = (times)[0]; if (-e <$DBM.*>) { $check = 1; } $cnt = unlink <$DBM.*>; if ($check && !$cnt) { &error("エラー","再生するにあたり、現DBMが削除できません."); } if (!open(SEQ,$SEQ)) { &error('SEQ Read Error','バックアップファイルが読めません.'); } if ($pl == 4) { if (!dbmopen(%DAT,$DBM,0666)) { &error('エラー',"$dbm_typeが開けませんでした."); } } else { if ($dbm_type eq 'GDBM_File') { eval 'use GDBM_File;'; } elsif ($dbm_type eq 'SDBM_File') { eval 'use SDBM_File;'; } elsif ($dbm_type eq 'ODBM_File') { eval 'use ODBM_File;'; } eval 'use Fcntl;'; eval 'tie %DAT,$dbm_type,$DBM,O_CREAT|O_RDWR,0666;'; if ($@ ne '') { &error('エラー',"$dbm_typeが開けませんでした.",$@); } } while () { if (/^#/ || /^$/) { next; } s/\n//; ($key,$val) = split(/\t/,$_,2); $DAT{$key} = $val; } if ($pl == 4) { dbmclose(%DAT); } else { eval 'untie %DAT;'; } close(SEQ); if (-e $lockfile) { unlink $lockfile; } $tend = time; $end = (times)[0]; print "Content-type: text/html\n\n"; print <<"EOF"; Utility $body

リストア終了

  • バックアップファイルからインデックスデータ(DBM)のみが再生成されました.

EOF printf "ACCESSTIME: %.3f seconds.
\n", $tend - $tstart; printf "CPUTIME: %.3f CPU seconds.

\n", $end - $start; print "[戻る]\n"; print "\n"; } sub limit { chdir($data_dir); @newls = (); $od_check = (eval { opendir(DIR,'.'); }, $@ eq ""); if (!$od_check) { $ls_check = (eval { $ls = `ls`; }, $@ eq ""); if (!$ls_check) { &error("致命的なエラー","一覧を抽出することができません."); } else { @ls = split(/\s+/,$ls); } } else { @ls = readdir(DIR); close(DIR); } foreach (@ls) { next if $_ eq '.'; next if $_ eq '..'; next if -d $_; next if $_ eq $seq_name; if (/(\d+)\.txt/) { push(@newls,$_); } } $all = @newls; @newls = sort { $b <=> $a; } @newls; if ($all > $full) { ($del_num,$ext) = split(/\./,"$newls[$#newls]"); delete $DAT{$del_num}; unlink("$newls[$#newls]"); pop(@newls); } chdir('..'); } sub error { if (-e $lockfile) { unlink $lockfile; } local (@msg) = @_; local ($i); print "Content-type: text/html\n\n"; print <<"EOF"; ERROR $body

$_[0]

EOF print "
    \n"; foreach $i (1 .. $#msg) { print "
  • $msg[$i]\n"; } print "
\n"; print <<"EOF";

[戻る]

EOF exit; } sub new { if (-e $lockfile) { unlink $lockfile; } @array = ( "47","49","46","38","39","61","16","00","0a","00","b3","02","00","00","00","00","ff","d6","00","ff", "ff","ff","ff","ff","ff","ff","ff","ff","ff","ff","ff","ff","ff","ff","ff","ff","ff","ff","ff","ff", "ff","ff","ff","ff","ff","ff","ff","ff","ff","ff","ff","ff","ff","ff","ff","ff","ff","ff","ff","ff", "ff","21","f9","04","01","00","00","02","00","2c","00","00","00","00","16","00","0a","00","40","04", "37","50","c8","29","02","ad","77","06","60","2b","0f","60","b8","71","00","e7","99","db","88","7e", "21","ab","81","65","1c","be","70","4d","8a","69","6e","66","38","98","49","29","e0","4d","b4","f3", "d8","8e","3e","c1","67","59","3b","a9","4a","47","54","12","c3","eb","09","22","00","3b"); print "Content-type: image/gif\n\n"; foreach (@array) { $data = pack('C*',hex($_)); print $data; } exit; } sub copyright { if (-e $lockfile) { unlink $lockfile; } @array = ( "47","49","46","38","39","61","27","00","1a","00","b3","00","00","00","00","00","ff","ff","ff","00", "00","00","00","00","00","00","00","00","00","00","00","00","00","00","00","00","00","00","00","00", "00","00","00","00","00","00","00","00","00","00","00","00","00","00","00","92","92","92","00","00", "00","21","f9","04","01","00","00","00","00","2c","00","00","00","00","27","00","1a","00","40","04", "b6","10","c8","49","ab","bd","d8","86","e0","b6","fb","1b","d7","01","5e","08","92","60","19","5c", "62","e7","b9","9f","2b","be","e1","86","d5","38","89","87","d2","6e","67","93","d6","88","33","eb", "f1","76","41","5e","c5","c7","6c","fa","58","29","14","cc","25","f5","9d","80","ad","df","2b","75", "bd","75","7b","d3","52","2a","2b","8c","6d","57","46","22","6d","cd","e6","ca","be","94","b2","59", "7e","da","c2","e3","f2","5a","6c","2e","e5","b3","da","6f","81","6c","44","40","69","3f","86","48", "19","35","1a","59","3a","73","34","58","70","62","81","63","6a","2a","32","25","1a","61","61","8e", "6f","26","82","4b","94","67","55","55","2a","45","14","7b","97","a9","a9","00","66","7b","ac","7f", "4e","4c","88","87","7f","63","b7","82","a6","54","8a","b8","9f","be","67","b5","50","bd","64","45", "9f","77","71","75","33","c5","ca","23","28","c1","41","ad","af","c6","a9","d3","51","85","d7","d8", "19","11","00","00","3b"); print "Content-type: image/gif\n\n"; foreach (@array) { $data = pack('C*',hex($_)); print $data; } exit; }