#!/usr/local/bin/perl ;#----------------------------------------------------- ;# 簡易マルチBBS - MiniBBS-kit ;# for UNIX/Perl4/Perl5/POST/SJIS (c)www.rescue.ne.jp ;#----------------------------------------------------- # [設置例] < >内はパーミッション値(推奨値ではない) (*)印..空のファイルを用意 # # |-- jcode.pl <644> 日本語コード変換ライブラリ v2.0以上を使うこと # |-- minibbs.cgi <755> 実行プログラム プログラム名.cgi (プログラム名は任意の半角英数字) ex. bbs.cgi board.cgi 0001.cgi # |-- log.message.cgi <666> メッセージファイル(*) ログ名.message.cgi (ログ名は任意の半角英数字) ex. bbs.message.cgi board.message.cgi 0001.message.cgi # |-- log.delete.cgi <666> 削除管理ファイル(*) ログ名.delete.cgi (ログ名は任意の半角英数字) ex. bbs.delete.cgi board.delete.cgi 0001.delete.cgi # |-- password.cgi <666> 管理者パスワードファイル(*) # # ※ 実行プログラムは1つ設置しておけば、ログを複数用意するだけで、複数の掲示板が運用できる. # ※ jperlまたはそれがパッチされているperlで動作させる場合は、jcode.pl(v2.0)は正常に動作しません. # # [呼び出し方法] # # http://設置した場所/プログラム名.cgi?log=ログ名 # 上記構成の場合 → http://設置した場所/minibbs.cgi?log=log # # [過去ログの利用方法] # # # http://設置した場所/ログモードにしたプログラム名.cgi?log=ログ名 # ログ(読出し専用)モード専用のプログラムを別に用意する # 呼び出し例 → pastlog.cgi?log=log # # ※ ログモード用に実行プログラムを1つ設置しておけば、複数のログを運用できる. # # [主な使い方] # # ・メッセージファイルの記録上限サイズを超えると、そのログには記録できない. # ・メッセージファイルをメモリ上に展開するので、上限サイズはあまり大きな数字にしないこと. # ・上限を超えたファイルは過去ログ化させ、新ログを用意する. # ・ただし、管理者パスワードを「パスワード」欄に入力すれば、上限を超えていても記録できる. # ・削除する場合に記入するパスワード欄に管理者パスワードを入力すれば、任意の記事の削除ができる. # ・削除した記事の番号は、削除管理ファイルに記録され、そこに記録された番号は一覧に表示されない. (論理削除) # ・従って、削除したい記事データがメッセージファイルから物理的に削除されているわけではない. # ・削除管理ファイルから番号を削除すれば、その番号の記事は一覧に表れる. #--------------------------------------------------------------------------------------- # ↓以下初期設定 #--------------------------------------------------------------------------------------- #● #--------------------------------------------------------------------------------------- # 名称 | Setup # Version | 1.00 # 処理名 | 初期設定 # $log = 0; #●ログモード(読出し専用)に 1:する 0:しない $bye = 'http://ホームページなどのURL/'; #●画面の「終了」リンク先(URL) $title_bar = '簡易BBS'; #●ブラウザのタイトルバーの名称 $body = ''; #●画面の色や背景の設定 (HTML書式) require './jcode.pl'; #●日本語コード変換ライブラリ(パス値) .. 2.0以上のバージョンのもの $pwd_file = './password.cgi'; #●管理者用パスワードファイル(パス値) $log_dir = "./"; #●各ログファイル(メッセージファイルと削除管理ファイル)を設置する場所(パス値) .. 同じ場所なら"./" $ext = "cgi"; #●各ログファイルの拡張子 .. 直接アクセスできないようにCGIを装える拡張子 $maxsize = 30000; #●メッセージファイルの記録上限サイズ(byte) .. あまり大きくしないこと $viewhost = 1; #●リモートホスト名を表示 1:する 0:しない $page = 10; #●1画面に一覧する記事件数 $cellcolor = "#ffeedd"; #●タイトル背景バーの色 $subject_color = "#333333"; #●タイトルの文字色 $info_color = "#555555"; #●付随情報(時刻,名前,ホスト名)の文字色 ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time); #●時刻取得 @wday_array = ('日','月','火','水','木','金','土'); #●曜日名の表現 $path = '/'; #●クッキーを扱う範囲 # ※ クッキーパスの指定の仕方 # もしあなたのホームページが http://www.foo.bar/~user/ なら、$path = '/~user/'; と設定しましょう. # もしあなたのホームページが http://www.foo.bar/ なら、$path = '/'; と設定しましょう. # 詳しいことは http://www.netscape.com/newsref/std/cookie_spec.html のpathの項目をご覧ください. &Main(); exit; #End_of_Setup #--------------------------------------------------------------------------------------- # ↓以下サブルーチン(順番は関係しない) #--------------------------------------------------------------------------------------- #● #--------------------------------------------------------------------------------------- # 名称 | Main # Version | 1.00 # 処理名 | メイン処理 # 動作内容 | 全体の流れを制御する # 引数 | なし # 戻り値 | なし # sub Main { if ($jcode'version < 2) { &Error('エラー','jcode.plは2.0以降のバージョンを設置してください.'); } &GetQuery(); &GetData(); &AdminSet(); &ReadCookie("$ENV{'SCRIPT_NAME'}\_$cmd{'log'}"); (@messages) = &ReadFile($message_file); &NoListSet($delete_file); if (@DELETE) { &Delete_Message; } if ($in{'action'} eq 'Write_Message') { &Write_Message; } &View_Message; }#End_of_Main #● #--------------------------------------------------------------------------------------- # 名称 | GetQuery # Version | 1.00 # 処理名 | クエリーデータ入力処理 # 動作内容 | クエリーデータの取得とログファイルの確定 # 引数 | なし # 戻り値 | なし(%cmd)($message_file..メッセージファイル名)($delete_file..削除管理ファイル名) # sub GetQuery { $cmd = $ENV{'QUERY_STRING'}; # 入力 @pairs = split(/&/,$cmd); foreach $pair (@pairs) { ($key,$val) = split(/=/,$pair); $val =~ tr/+/ /; $val =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C",hex($1))/eg; # URLデコード $cmd{$key} = $val; } if ($cmd{'img'} eq 'copyright') { &Copyright; } # アイコン画像生成へ elsif ($cmd{'log'} eq '') { &Error("Not Found","ログ名が指定されていません.","Usage http://$ENV{'SERVER_NAME'}$ENV{'SCRIPT_NAME'}?log=ログ名"); } $message_file = "$log_dir$cmd{'log'}\.message\.$ext"; # メッセージファイル名の取得 $delete_file = "$log_dir$cmd{'log'}\.delete\.$ext"; # 削除管理ファイル名の取得 if (!-e $message_file) { &Error("Not Found","メッセージファイルが見つかりません."); } if (!-e $delete_file) { &Error("Not Found","削除管理ファイルが見つかりません."); } }#End_of_GetQuery #● #--------------------------------------------------------------------------------------- # 名称 | GetData # Version | 1.00 # 処理名 | 標準データ入力処理 # 動作内容 | フォームから入力されたデータの取得 # 引数 | なし # 戻り値 | なし(%in)($preview..プレビュー命令)(@DELETE..削除番号) # sub GetData { read(STDIN,$buffer,$ENV{'CONTENT_LENGTH'}); # 入力 @pairs = split(/&/,$buffer); foreach $pair (@pairs) { ($key,$val) = split(/=/,$pair); $key =~ tr/+/ /; $key =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C",hex($1))/eg; $val =~ tr/+/ /; $val =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C",hex($1))/eg; &jcode'h2z_sjis(*val); # 半角カナ→全角(SJIS)変換 &jcode'convert(*val,'sjis'); # SJIS変換 if ($key eq 'preview') { $preview = 1; next; } # プレビュー処理の検知 elsif ($key =~ /Next:(\d+)/) { $in{'page_control'} = $1; last; } # 次のページの読み飛ばし行数 elsif ($key eq 'delete') { push(@DELETE,"$val\n"); next; } # 削除番号を取得 $val =~ s/\t//g; # タブコードを無効 $val =~ s/\r\n/\r/g; # Win → Mac (文中の改行はCRとする/行の終わりはLF) $val =~ s/\n/\r/g; # Unix → Mac $val =~ s/&/&/g; # タグ禁止 $val =~ s/"/"/g; $val =~ s//>/g; $in{$key} = $val; # 入力データは%inへ } }#End_of_GetData #● #--------------------------------------------------------------------------------------- # 名称 | AdminSet # Version | 1.00 # 処理名 | 管理者パスワードの設定関連 # 動作内容 | フォームから入力されたデータの取得とログファイルの確定 # 引数 | なし # 戻り値 | なし # sub AdminSet { if (!-e $pwd_file) { &Error("エラー","管理者パスワード記録用ファイルが見つかりません."); } if ($in{'action'} eq 'Set_MasterPassword') { &Set_MasterPassword; } # 管理者パスワード記録へ if (-z $pwd_file || $cmd{'action'} eq 'PasswordForm') { &PasswordForm; } # 管理者パスワードの設定画面へ }#End_of_AdminSet #● #--------------------------------------------------------------------------------------- # 名称 | ReadCookie # Version | 1.00 # 処理名 | クッキーデータの取得 # 動作内容 | レスキュー形式のクッキーデータを展開する # 引数 | クッキー名 # 戻り値 | なし(%COOKIE) # sub ReadCookie { local($cname) = @_; $cookies = $ENV{'HTTP_COOKIE'}; # 入力 @pairs = split(/;/,$cookies); # 独自(レスキュー)形式のデータの展開 項目名1:内容1,項目名2:内容2,... foreach $pair (@pairs) { ($key,$val) = split(/=/,$pair,2); $key =~ s/ //g; if ($key eq $cname) { @pairs = split(/,/,$val); foreach $pair (@pairs) { ($key,$val) = split(/:/,$pair,2); $COOKIE{$key} = $val; } last; } } }#End_of_ReadCookie #● #--------------------------------------------------------------------------------------- # 名称 | ReadFile # Version | 1.00 # 処理名 | ファイル入力 # 動作内容 | ファイルの内容をメモリに読み込んで順番を逆にする # 引数 | ファイル名 # 戻り値 | 配列 # sub ReadFile { local($file) = @_; local(@lines); if (!open(IN,$file)) { &Error('エラー','メッセージファイルが見つかりません.'); } @lines = ; # 配列に一度に読み入れる(大きなファイルで実行しないこと) @lines = reverse @lines; # 順番を入れ替える(新しい順に表示するため) close(IN); return @lines; }#End_of_ReadFile #● #--------------------------------------------------------------------------------------- # 名称 | NoListSet # Version | 1.00 # 処理名 | 削除番号処理 # 動作内容 | 論理削除番号を定義する # 引数 | 削除管理ファイル名 # 戻り値 | なし(%deleted) # sub NoListSet { local($file) = @_; if (!open(IN,$file)) { &Error('エラー','削除番号ファイルが見つかりません.'); } while ($number = ) { # 1行ずつ読み込む $number =~ s/\n//; # 改行を削除 if ($number =~ /^#|^$/) { next; } # コメント行と空行を読み飛ばす if ($number =~ /\D/) { next; } # 記事番号(非数字)以外は読み飛ばす $number =~ s/\n//; # 改行を削除 $deleted{$number} = 1; # 削除番号をキーとした連想配列を定義する } close(IN); }#End_of_NoListSet #● #--------------------------------------------------------------------------------------- # 名称 | Search # Version | 1.00 # 処理名 | 抽出処理 # 動作内容 | 読み込まれたログを抽出する # 引数 | なし # 戻り値 | なし # sub Search { $page_control = $hit = 0; foreach $line (@messages) { $page_control++; if ($page_control < $in{'page_control'}) { next; } # 指定の行数まで読み飛ばす ($number,$i) = split(/\t/,$line,2); # 番号を取り出す if ($deleted{$number} == 1) { next; } # 表示しない(削除)記事を読み飛ばす if ($hit != $page) { push(@MESSAGE,$line); $hit++; } # 1ページ($page件)に満たない場合は抽出して件数を数える else { $next_control = $page_control; last; } # 達したら、ここまで至った行数を次の読み飛ばし行数として指定して終了 } }#End_of_Search #● #--------------------------------------------------------------------------------------- # 名称 | View_Message # Version | 1.00 # 処理名 | 一覧処理 # 動作内容 | 抽出されたログを表示する # 備考 | 画面内に文字を挿入したい場合は、このサブルーチン内の◆印(2箇所用意)の場所に直接書きこむ # 引数 | なし # 戻り値 | なし # sub View_Message { &Search; # 抽出処理へ if (!$log) { # ログモード以外はカーソルフォーカス位置の決定 if ($COOKIE{'NAME'} eq '') { $js = 'onLoad="document.WriteForm.NAME.focus();"'; } elsif ($COOKIE{'EMAIL'} eq '') { $js = 'onLoad="document.WriteForm.EMAIL.focus();"'; } else { $js = 'onLoad="document.WriteForm.TITLE.focus();"'; } $body =~ s/タグに挿入 } if ($in{'page_control'} != 0) { $jsback = '〔前に戻る〕'; } # 最初の画面以外は戻るリンクを用意 if (!$log) { # ログモード以外は空き容量の処理 $size = -s $message_file; # メッセージファイルのサイズ取得 $free = $maxsize - $size; # 最大記録サイズとの差を得る if ($free < 0) { $free = 0; } # 空き容量がマイナス表示にならないようにする 1 while $free =~ s/(.*\d)(\d\d\d)/$1,$2/g; # 桁カンマを入れる処理 if ($size > $maxsize) { $free = "《書き込み不可》空き容量 $free bytes free"; } else { $free = "空き容量 $free bytes free"; } } else { $free = "《ログモード》"; } #------------------------------------------------------------------------------- # 表示処理 &Html_head; # ヘッダの出力 print "$body\n"; # ◆ ↓画面上部に挿入する文字列(HTML形式)は、print <<'EOF'; の次の行から EOF の直前までの間に直接書いてください. print <<'EOF';

簡易マルチBBS

EOF if (!$log) { # ログモードでない場合は投稿フォームを用意 # ↓投稿フォーム print <<"EOF";

名前 保存
Eメール 保存
タイトル
本文 改行無効 改行有効 図/表\モード

内容にURLがあればリンクさせる
パスワード 保存      

EOF } # ↓更新・終了リンク print <<"EOF";


$jsback〔更新〕〔終了 $free

EOF $err = join (',',@ERR); if (@ERR) { print "※ 次の番号の記事は削除できませんでした: $err
\n"; print "※ 理由 (A)パスワードが違います (D)既に削除されています (N)存在しない番号または異常です
\n"; } print <<"EOF";

EOF foreach $message (@MESSAGE) { $message =~ s/\n//; # 改行を削除 ($number,$pwd,$date,$name,$email,$host,$title,$mode,$link,$reserve,$value) = split(/\t/,$message,11); if ($email =~ /\b[-\w.]+@[-\w.]+\.[-\w]+\b/) { $name = "$name <$email>"; } # Eメール記載があればリンクする if ($host eq '') { $viewhost = 0; } # ホスト名の記録がない場合は表示しない if (!$viewhost) { $host = ''; } else { $host = "- $host"; } if (!$log) { $delsw = "削除"; } else { $delsw = ""; } # ↓記事一覧 print <<"EOF";
【$number】$title
$date - $name $host $delsw

EOF if ($link) { # URLをリンクする $value =~ s/>/\t/g; $value =~ s/(https?|ftp|gopher|telnet|whois|news)\:([\w|\:\!\#\$\%\=\&\-\^\`\\\|\@\~\[\{\]\}\;\+\*\,\.\?\/]+)/$1\:$2<\/a>/ig; $value =~ s/\t/>/g; } if ($mode == 0) { print "

"; } # 図/表モード(0)

		if ($mode == 0) { print $value; } # 図/表モード(0)
		elsif ($mode == 1) { $value =~ s/\r/
\r/g; print $value; } # 改行有効(1) else { $value =~ s/\r//g; print $value; } # 改行無効(2) if ($mode == 0) { print "

\n"; } # 図/表モード(0) print "

\n"; } # ↓次のページ・削除ボタン print <<"EOF";


EOF if ($next_control ne '') { print "\n"; } if (@MESSAGE && !$log) { print "\n"; } if ($in{'page_control'} != 0) { print "\n"; } print <<"EOF";
パスワード $jsback

EOF # ◆ ↓この位置が、画面下部となる. print <<'EOF'; EOF print <<"EOF"; [管理用]

MiniBBS-kit

EOF }#End_of_View_Message #● #--------------------------------------------------------------------------------------- # 名称 | Write_Message # Version | 1.00 # 処理名 | 記録処理 # 動作内容 | 投稿されたデータを記録する # 引数 | なし # 戻り値 | なし # sub Write_Message { ($admin) = &CheckAdmin($in{'PASSWD'}); if (-s $message_file > $maxsize && !$admin) { &Error("記録不可","記録ファイルのサイズが、記録できる最大値を超えましたので投稿できません. (管理者を除く)"); } if ($in{'NAME'} eq '' || $in{'NAME'} =~ /[\<\>\,\;\:]/) { &Error("未記入があります","名前を入力してください.","\<\>\,\;\:は使えません."); } if ($in{'EMAIL'} ne '' && !($in{'EMAIL'} =~ /\b[-\w.]+@[-\w.]+\.[-\w]+\b/)) { &Error("未記入があります","Eメールを半角で正しく入力してください."); } if ($in{'TITLE'} eq '') { &Error("未記入があります","タイトルを入力してください."); } if ($in{'VALUE'} eq '') { &Error("未記入があります","本文を入力してください."); } if ($in{'PASSWD'} eq '' || $in{'PASSWD'} =~ /\W/ || length($in{'PASSWD'}) < 6) { &Error("未記入があります","6文字以上のパスワードを半角英数字で入力してください.","投稿するこの記事を削除するためのパスワードです."); } #ホスト名の取得(リムネットなら次の4行を指定のプログラムと入れ替える) $host = $ENV{'REMOTE_HOST'}; $addr = $ENV{'REMOTE_ADDR'}; if ($host eq '') { $host = $addr; } # ホスト名にIPが入らない場合があるので if ($host eq $addr) { $host = gethostbyaddr(pack('C4',split(/\./,$host)),2) || $addr; } if ($preview) { &Preview; } # プレビュー処理へ # 既に記録された最新のデータと比較 $sample = "$in{'NAME'}\t$in{'EMAIL'}\t$in{'TITLE'}\t$in{'MODE'}\t$in{'LINK'}\t$in{'VALUE'}"; if ($messages[0] ne '') { $messages[0] =~ s/\n//; } # 改行を削除 ($number2,$pwd2,$date,$name,$email,$host2,$title,$mode,$link,$reserve,$value) = split(/\t/,$messages[0],11); $target = "$name\t$email\t$title\t$mode\t$link\t$value"; if ($sample eq $target) { return; } ($number,$i) = split(/\t/,$messages[0],2); # 最大番号を取り出す $number ++; # 番号を+1する $date_now = sprintf("%04d/%01d/%01d(%s)%02d:%02d",$year +1900,$mon +1,$mday,$wday_array[$wday],$hour,$min); # 時刻構成 ($pwd) = &MakeCrypt($in{'PASSWD'}); # パスワードの暗号化 $new = "$number\t$pwd\t$date_now\t$in{'NAME'}\t$in{'EMAIL'}\t$host\t$in{'TITLE'}\t$in{'MODE'}\t$in{'LINK'}\t\t$in{'VALUE'}\n"; # 記録するデータ unshift(@messages,$new); # 既に読み込んだデータ(逆順済み)の先頭に追加する if (!open(OUT,">> $message_file")) { &Error('エラー','メッセージファイルが見つからないか、記録できません.'); } # 追加書込モードで記録する print OUT $new; close(OUT); if (!$in{'cookie1'} && !$in{'cookie2'} && !$in{'cookie3'}) { $date_gmt = "Sun, 01-Jan-1995 01:00:00 GMT"; } # クッキーを削除するには正確な過去を設定する else { ($secg,$ming,$hourg,$mdayg,$mong,$yearg,$wdayg,$ydayg,$isdstg) = gmtime(time + 30*24*60*60);# 期限を30日後に設定(GMT) $y0="Sunday"; $y1="Monday"; $y2="Tuesday"; $y3="Wednesday"; $y4="Thursday"; $y5="Friday"; $y6="Saturday"; $m0="Jan"; $m1="Feb"; $m2="Mar"; $m3="Apr"; $m4="May"; $m5="Jun"; $m6="Jul"; $m7="Aug"; $m8="Sep"; $m9="Oct"; $m10="Nov"; $m11="Dec"; @youbi = ($y0,$y1,$y2,$y3,$y4,$y5,$y6); @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); if ($in{'cookie1'}) { $COOKIE{'NAME'} = $in{'NAME'}; } else { $COOKIE{'NAME'} = ""; } # 保存する項目の処理 if ($in{'cookie2'}) { $COOKIE{'EMAIL'} = $in{'EMAIL'}; } else { $COOKIE{'EMAIL'} = ""; } if ($in{'cookie3'}) { $COOKIE{'PASSWD'} = $in{'PASSWD'}; } else { $COOKIE{'PASSWD'} = ""; } } print "Set-Cookie: $ENV{'SCRIPT_NAME'}\_$cmd{'log'}=NAME:$COOKIE{'NAME'}\,EMAIL:$COOKIE{'EMAIL'}\,PASSWD:$COOKIE{'PASSWD'}; path=$path; expires=$date_gmt\n"; # クッキーをセット }#End_of_Write_Message #● #--------------------------------------------------------------------------------------- # 名称 | Preview # Version | 1.00 # 処理名 | プレビュー処理 # 動作内容 | 投稿前に表示状態を再現する # 引数 | なし # 戻り値 | なし # sub Preview { if ($in{'NAME'} eq '') { &Error("未記入があります","名前を入力してください."); } if ($in{'EMAIL'} ne '' && !($in{'EMAIL'} =~ /\b[-\w.]+@[-\w.]+\.[-\w]+\b/)) { &Error("未記入があります","Eメールを半角で正しく入力してください."); } if ($in{'TITLE'} eq '') { &Error("未記入があります","タイトルを入力してください."); } if ($in{'VALUE'} eq '') { &Error("未記入があります","本文を入力してください."); } if ($in{'EMAIL'} =~ /\b[-\w.]+@[-\w.]+\.[-\w]+\b/) { $in{'NAME'} = "$in{'NAME'} <$in{'EMAIL'}>"; } # Eメール記載があればリンクする if ($host eq '') { $viewhost = 0; } # ホスト名が取得できない場合は表示しない if (!$viewhost) { $host = ''; } else { $host = "- $host"; } &Html_head; print "$body\n"; print <<"EOF";
《表\示確認》 $in{'TITLE'}
EOF print <<"EOF";

  • by $in{'NAME'} $host

EOF if ($in{'LINK'}) { # URLをリンクする $in{'VALUE'} =~ s/>/\t/g; $in{'VALUE'} =~ s/(https?|ftp|gopher|telnet|whois|news)\:([\w|\:\!\#\$\%\=\&\-\^\`\\\|\@\~\[\{\]\}\;\+\*\,\.\?\/]+)/$1\:$2<\/a>/ig; $in{'VALUE'} =~ s/\t/>/g; } if ($in{'MODE'} == 0) { print "

"; } # 図/表モード(0)

	if ($in{'MODE'} == 0) { print $in{'VALUE'}; } # 図/表モード(0)
	elsif ($in{'MODE'} == 1) { $in{'VALUE'} =~ s/\r/
\r/g; print $in{'VALUE'}; } # 改行有効(1) else { $value =~ s/\r//g; print $in{'VALUE'}; } # 改行無効(2) if ($in{'MODE'} == 0) { print "

\n"; } # 図/表モード(0) print <<"EOF";


投稿画面に戻る

EOF exit; }#End_of_Preview #● #--------------------------------------------------------------------------------------- # 名称 | Delete_Message # Version | 1.00 # 処理名 | 削除処理 # 動作内容 | 投稿されたデータを削除(論理削除)する # 引数 | なし # 戻り値 | なし # sub Delete_Message { if (!@DELETE) { return; } # 1つもチェックされていなければ処理しない foreach $message (@messages) { # 記事の存在とパスワードを処理するための準備 ($number,$pwd,$i) = split(/\t/,$message,3); $check_pwd{$number} = $pwd; # 番号をキー、値をパスワードにした連想配列%check_pwdを用意 } ($admin) = &CheckAdmin($in{'PASSWD'}); # 管理者パスワードかどうか? if ($admin) { # 管理者パスワードの場合は簡易チェックのみ foreach $number (@DELETE) { $number =~ s/\n//; # 改行コードを削除 if ($check_pwd{$number} eq '') { push(@ERR,"$number(N)"); } # 存在しない記事番号 elsif ($deleted{$number}) { push(@ERR,"$number(D)"); } # 削除済み else { $deleted{$number} = 1; # 削除指定記事とする push(@DELETE_OK,"$number\n"); # 削除する記事番号 } } } else { # 管理者パスワードでない場合は記事のパスワードと照合 foreach $number (@DELETE) { $number =~ s/\n//; if ($check_pwd{$number} eq '') { push(@ERR,"$number(N)"); next; } # 存在しない記事番号 elsif ($deleted{$number}) { push(@ERR,"$number(D)"); next; } # 削除済み if ($check_pwd{$number} =~ /^\$1\$/) { $salt = 3; } else { $salt = 0; } if (crypt($in{'PASSWD'},substr($check_pwd{$number},$salt,2)) eq $check_pwd{$number}) { $deleted{$number} = 1; push(@DELETE_OK,"$number\n"); } # 認証 else { push(@ERR,"$number(A)"); } } } if (!open(OUT,">> $delete_file")) { &Error('エラー','削除管理ファイルが見つからないか、記録できません.'); } # 追加書込モードで記録する print OUT @DELETE_OK; close(OUT); undef %check_pwd; # メモリ開放 }#End_of_Delete_Message #● #--------------------------------------------------------------------------------------- # 名称 | Html_head # Version | 1.00 # 処理名 | 画面出力用ヘッダ # 動作内容 | # 引数 | なし # 戻り値 | なし # sub Html_head { $title_bar =~ s/\n//g; print "Content-type: text/html\n\n"; print <<"EOF"; $title_bar EOF }#End_of_Html_head #● #--------------------------------------------------------------------------------------- # 名称 | PasswordForm # Version | 1.00 # 処理名 | 管理者パスワードの設定と変更 # 動作内容 | 設定フォーム画面 # 引数 | なし # 戻り値 | なし # sub PasswordForm { if ($log) { &Error("エラー"); } # 過去ログモード時は処理しない &Html_head; print <<"EOF"; $body

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

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


EOF if (!-z $pwd_file) { print "[戻る]

\n"; } print "\n"; exit; }#End_of_PasswordForm #● #--------------------------------------------------------------------------------------- # 名称 | Set_MasterPassword # Version | 1.00 # 処理名 | 管理者パスワードの記録処理 # 動作内容 | # 引数 | なし # 戻り値 | なし # sub Set_MasterPassword { if ($log) { &Error("エラー"); } # 過去ログモード時は処理しない if (!-z $pwd_file) { if (!open(READ,$pwd_file)) { &Error('エラー','管理者用パスワードファイルが読み出せません.'); } $master = ; close(READ); $master =~ s/\n//g; 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); }#End_of_Set_MasterPassword #● #--------------------------------------------------------------------------------------- # 名称 | CheckAdmin # Version | 1.00 # 処理名 | 管理者確認 # 動作内容 | 管理者パスワードのチェック # 引数 | 管理者パスワードの平文 # 戻り値 | 認証されれば1 されない場合は0 # sub CheckAdmin { local($input) = @_; local($admin); $admin = 0; if (!open(READ,$pwd_file)) { &Error('エラー','管理者用パスワードファイルが読み出せません.'); } $master = ; close(READ); $master =~ s/\n//; if ($master =~ /^\$1\$/) { $salt = 3; } else { $salt = 0; } if ($master eq '' || $input eq '') { ; } elsif (crypt($input,substr($master,$salt,2)) eq $master) { $admin = 1; } # 認証できたら$adminを定義 return $admin; }#End_of_CheckAdmin #● #--------------------------------------------------------------------------------------- # 名称 | MakeCrypt # Version | 1.00 # 処理名 | 暗号処理 # 動作内容 | 文字列の暗号化 # 引数 | 平文 # 戻り値 | 暗号 # sub MakeCrypt { local($plain) = @_; # 入力:平文 local(@char,$f,$now,@saltset,$pert1,$pert2,$nsalt,$salt); @saltset = ('a'..'z','A'..'Z','0'..'9','.','/'); # 暗号が構成される文字群 $now = time; # ↓この辺は通称「らくだの本」を参照 srand(time|$$); $f = splice(@saltset,rand(@saltset),1) . splice(@saltset,rand(@saltset),1); ($pert1,$pert2) = unpack("C2",$f); $week = $now / (60*60*24*7) + $pert1 + $pert2 - length($plain); $nsalt = $saltset[$week % 64] . $saltset[$now % 64]; $result = crypt($plain,$nsalt); if ($result =~ /^\$1\$/) { $salt = 3; } else { $salt = 0; } if (crypt($plain,substr($result,$salt,2)) ne $result || $result eq '') { &Error("暗号処理エラー","パスワードの暗号化に失敗しました.","戻って再度実行してください."); } # 稀に暗号処理が正しくされていない場合があるので return $result; # 戻値:暗号 }#End_of_MakeCrypt #● #--------------------------------------------------------------------------------------- # 名称 | Error # Version | 1.00 # 処理名 | エラー処理 # 動作内容 | エラーメッセージの表示とプログラム終了 # 引数 | エラーメッセージ # 戻り値 | なし # sub Error { local (@msg) = @_; local ($i); &Html_head; print <<"EOF"; $body

$msg[0]

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

[戻る]

EOF exit; }#End_of_Error #● #--------------------------------------------------------------------------------------- # 名称 | Copyright # Version | 1.00 # 処理名 | ロゴの出力 # 動作内容 | MiniBBSロゴのイメージ生成 # 引数 | なし # 戻り値 | なし # sub Copyright { @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; }#End_of_Copyright