#!/usr/local/bin/perl ;# ↑このパスはプロバイダによって違うので調べて設定する ;# (これはこのスクリプトの最初の1行になければならず、それより上に空行があってもいけません。) ;# (この設定が必要ないサーバもあります。一般的な場合を想定して解説を入れています。) ;# (他に、プロバイダからアナウンスされている情報を十分理解しておいてください。) ;# ;# 簡易BBS version 8.95t(フリーソフト) ;# ;# This is Freeware. ;# (c)1996-2008 by CGI-RESCUE ;# Created on: 05/FEB/1996 ;# Scripts Found at: http://www.rescue.ne.jp/ ;# [History] ;# 11/AUG/98 v8.82s バグフィックス ;# 27/SEP/98 v8.83t 画像アップロード機能 ;# 06/MAR/99 v8.84t 削除認証ミスでも画像削除されてしまうバグの修正 ;# 28/APR/99 v8.85t 最大登録を超えた記事の画像が削除されないバグの修正 ;# 14/JUL/99 v8.90t 画像以外のファイルを扱えるようにした ;# (ただし、バージョンアップ後にはそれ以前の画像は表示されずリンクになります) ;# 20/JUL/99 v8.91t クッキーの2000年対応 ;# 23/JUL/99 v8.92t 初期設定変更(URLとパスの関係が別の場合に対応) ;# 26/Jul/02 v8.93t クロスサイトスクリプティングに対する対応 ;# 13/Dec/08 v8.95t クロスサイト・スクリプティング脆弱性を修正(※1) ########################################################################################### # # ■重要! v8.82sとデータの互換性はありません。 # # 基本構成(初期設定はこの構成を前提に解説します) # # public_html/(ホームページディレクトリ) # | # |-- cgi-bin/ # | # |-- jcode.pl (644) # |-- cgi-lib217.pl (644) # |-- minibbs.cgi (755) # |-- minibbs_dat.cgi (666) # |-- minibbs_sus.cgi (666) # |-- images/ (777) # ########################################################################################### #----------------# # 初期設定 # #----------------# #--- 必ずあなたの環境に合わせて書き替える項目 --------------------------------------------# #◆掲示板の名前 # ''内に記述しますが、'を入れたい場合は '' を "" に替えてください. # ただしその場合、文字によって化けが生じることがあります. # 詳しくは当サイトのFAQを参照してください. $title = '簡易BBS'; #◆このスクリプトをURLで設定 $reload = 'http://設置したURL/minibbs.cgi'; #◆画面の「終了」リンク先をURLで設定 $modoru = 'http://ホームページなどのURL/'; #--- 必要に応じて設定する項目 ------------------------------------------------------------# #◆画面の色や背景の設定 (HTML書式) $body = ''; #◆タイトル背景の設定 $title_back = '#ffaaaa'; #◆タグを使えるようにするかどうかの設定 # についてはURLフォームが用意してあるので、いたずらやタグの # 閉じ忘れ等による混乱を避けるためにできるだけ使えないようにしておくことをお勧めします. # 使える:1 使えない:0 $tag = 0; #◆タグを使えるようにした場合に拒否する危険にもなるタグの指定 # $tag = 0;にした場合はこのままでよい. @kyohi = ( '', '', '', '', '', '', '', '', '', '', '', '', ''); #◆画面内に記述する文字列等 (HTML書式) # ''内に記述しますが、'を入れたい場合は '' を "" に替えてください. # ただしその場合、文字によって化けが生じることがあります. # 詳しくは当サイトのFAQを参照してください. # 必要ない場合は '' 内に何も書きません. #◇タイトルの下位置に表示する文字列 $msg_top1 = ''; $msg_top2 = ''; $msg_top3 = ''; #◇投稿フォームの下位置に表示する文字列 $msg_mid1 = '■記事の削除は管理者にご依頼ください.
'; $msg_mid2 = '■入力欄に記憶された内容を消去するには、[書き直し]→[書き込む]を順番に押します.'; $msg_mid3 = ''; #◇最下部に表示する文字列 $msg_btm1 = '■マスターキー(管理者)によってのみ記事の削除が可能です.'; $msg_btm2 = ''; $msg_btm3 = ''; #◆$reloadで設定した設置URL以外のフォームからの投稿を禁止する処置 する:1 しない:0 # 悪戯の防止用ですが、利用サーバやブラウザによっては正規投稿もできなくなる場合もあります. $ref_axs = 0; #◆1記事の最大記録サイズ(bytes) 0で無制限 $max_size = 0; #◆1画面に表示する記事件数 $def = 10; #◆入力形式の設定(大文字で) 標準入力:POST その他:GET # 投稿ボタンを押して Method not implemented.. 等というエラーが出る場合は GET で試すこと $method = "POST"; #◆画像アップロード する(標準入力の場合のみ有効):1 しない:0 $upload = 1; #◆書き込み件数の最大登録数の設定です。この件数を超えると、古いものから削除されていきます. # ページ処理機能が付きましたので、この件数を大きくしても一度に表示される記事数は限定されます. # 記録されたファイルの巨大化を防止する為に、ある程度の件数で自動削除されるようにします. # サーバ負荷を考慮して、あまり大きくしないことが重要です. $max = 100; #◆日本語コード変換ライブラリ # minibbs.cgiと同じ場所に設置する場合はこのままでよい. require './jcode.pl'; #◆マルチパート対応CGIライブラリ require "./cgi-lib217.pl"; #◆内容が書き込まれる記録ファイルのパスを設定 # minibbs.cgiと同じ場所に設置する場合はこのままでよい. $file = './minibbs_dat.cgi'; #◆投稿依頼内容のファイルのパスを設定 # minibbs.cgiと同じ場所に設置する場合はこのままでよい. $sus = './minibbs_sus.cgi'; #■画像ディレクトリの設定 # imagesという名前のディレクトリを作成し、パーミッションを777にし、$tmpにその場所を設定する $tmp = "./images/"; # 内部パスで $tmp_url = "./images/"; # URLで #◆時刻取得調整 # gmtime()は世界標準時であるので、日本時間は+9時間となる. # 海外サーバの場合は調整すること. ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time); #◆クッキーの消化設定 # 最終書き込みから 30日後 30*24*60*60 #           1日後 24*60*60 #          10時間後 10*60*60 ($secg,$ming,$hourg,$mdayg,$mong,$yearg,$wdayg,$ydayg,$isdstg) = gmtime(time + 30*24*60*60); #-------------------------------------------------------------------------------------------- if ($method ne 'POST') { $upload = 0; } @wday_array = ('日','月','火','水','木','金','土'); $date_now = sprintf("%01d月%01d日(%s)%02d時%02d分",$mon +1,$mday,$wday_array[$wday],$hour,$min); $date_num = sprintf("%02d%02d%02d%02d%02d",$mon +1,$mday,$hour,$min,$sec); # <-変更禁止 $ret = &ReadParse; if ($ENV{'CONTENT_TYPE'} eq 'application/x-www-form-urlencoded' || $ENV{'REQUEST_METHOD'} eq 'GET') { foreach $pair (@in) { ($name,$value) = split(/=/,$pair); $name =~ s/%([A-Fa-f0-9]{2})/pack("c",hex($1))/ge; $value =~ s/%([A-Fa-f0-9]{2})/pack("c",hex($1))/ge; &jcode'convert(*value,'sjis'); $value =~ s///g; $value =~ s/\t//g; if ($tag) { foreach (@kyohi) { if (/^$/) { next; } if ($value =~ /$_/i) { &error('記入ミス','記録できないタグ記述があります.'); } } } else { $value =~ s//>/g; } if ($FORM{'action'} eq 'regist') { if ($value =~ /\r\n/) { $value =~ s/\r\n/\r/g; } if ($value =~ /\n/) { $value =~ s/\n/\r/g; } if ($FORM{'how'} eq '1') { $value =~ s/\r/
\r/g; } } if ($name eq 'name' || $name eq 'email') { $value =~ s/\;//g; $value =~ s/\://g; $value =~ s/\,//g; } if ($name eq 'target') { push(@RM,$value); } else { $FORM{$name} = $value; } } } else { foreach (@in) { ($fname) = $_ =~ /\bfilename="([^"]*)"/i; ($fname) = $_ =~ /\bfilename=([^\s:;]+)/i unless defined $fname; ($name) = $_ =~ /\bname="([^"]+)"/i; ($name) = $_ =~ /\bname=([^\s:;]+)/i unless defined $name; ($ctype) = $_ =~ /\s*Content-type:\s*"([^"]+)"/i; ($ctype) = $_ =~ /\s*Content-Type:\s*([^\s:;]+)/i unless defined $ctype; if ($fname ne '') { $fname = reverse("$fname"); ($fname) = split(/\\|\/|\:/,$fname); $fname = reverse("$fname"); if ($fname ne '' && $ctype eq '') { &error('エラー',"$fnameは取扱できないデータです."); } if (-e "$tmp$fname") { &error('エラー','同名のファイルが存在していますので名前を変えてください.'); } $FORM{'file'} = $fname; $FORM{'ctype'} = $ctype; $in{'file'} = $in{$name}; } elsif (/\bfilename=/i) { $FORM{'file'} = ""; } else { $value = $in{$name}; &jcode'convert(*value,'sjis'); $value =~ s///g; $value =~ s/\t//g; if ($tag) { foreach (@kyohi) { if (/^$/) { next; } if ($value =~ /$_/i) { &error('記入ミス','記録できないタグ記述があります.'); } } } else { $value =~ s//>/g; } if ($FORM{'action'} eq 'regist') { if ($value =~ /\r\n/) { $value =~ s/\r\n/\r/g; } if ($value =~ /\n/) { $value =~ s/\n/\r/g; } if ($FORM{'how'} eq '1') { $value =~ s/\r/
\r/g; } } if ($name eq 'name' || $name eq 'email') { $value =~ s/\;//g; $value =~ s/\://g; $value =~ s/\,//g; } if ($name eq 'target') { push(@RM,$value); } else { $FORM{$name} = $value; } } } } $FORM{'url'} =~ s/"/"/g; if (!open(DB,"$file")) { &error('設定ミス',"$file が設定された場所にありません."); } @lines = ; close(DB); $password = shift(@lines); $password =~ s/\n//g; ($header,$password) = split(/:/,$password); if ($password =~ /^\$1\$/) { $salt = 3; } else { $salt = 0; } if ($FORM{'pwd'} ne '' && crypt($FORM{'pwd'}, substr($password,$salt,2)) eq $password) { $admin = 1; } if ($FORM{'action'} eq 'password') { &encode; } if (-z $file) { $first = 1; &password; exit; } elsif ($header ne 'MiniBBSv8t') { &error('データエラー',"$file のデータ形式はこの簡易BBSでは使えません."); } $cookies = $ENV{'HTTP_COOKIE'}; @pairs = split(/;/,$cookies); foreach $pair (@pairs) { ($name, $value) = split(/=/, $pair); $name =~ s/ //g; $value =~ s/\"/"/g; # ※1 $value =~ s//>/g; $DUMMY{$name} = $value; } @pairs = split(/,/,$DUMMY{$reload}); foreach $pair (@pairs) { ($name, $value) = split(/:/, $pair); $COOKIE{$name} = $value; } if ($FORM{'admin'} eq 'change') { &password; exit; } elsif ($FORM{'admin'} eq 'sus' && $admin) { &sus_list; exit; } elsif ($FORM{'admin'} eq 'move' && $admin) { &move; } if ($FORM{'action'} eq 'remove') { &remove; } elsif ($FORM{'action'} eq 'regist') { ®ist; exit; } &html; exit; sub html { #--- 入力フォーム画面 --------------------------------# # ここからが画面です。もし空いている部分に画像等を入れたい場合は、 # print '' . "\n"; という構文の中にタグを入れて、好きな位置に挿入してください。 # 例:をタイトルの下に入れたい場合は、print "

$title

\n"; の # 下に、print '' . "\n"; を記述します。 # これでわからない場合は、PERLプログラミングを勉強してください。 print "Content-type: text/html\n\n"; print "$title\n"; print "$body\n"; print "

$title

\n"; print "$msg_top1\n"; print "$msg_top2\n"; print "$msg_top3

\n"; if ($upload) { $enc = 'ENCTYPE="multipart/form-data"'; } print "

\n"; print "\n"; print "投稿者
\n"; print "メール
\n"; print "題 名
\n"; print "内 容 自動スペース&自動改行 "; print "入力した通りに表\示
\n"; print "   
\n"; print "リンク
\n"; if ($upload) { print "送 信

\n"; } else { print "

\n"; } print "    "; print "

"; print "$msg_mid1\n"; print "$msg_mid2\n"; print "$msg_mid3

\n"; #--- 記録記事の出力 ----------------------------------# @lines = reverse(@lines); if ($FORM{'page'} eq '') { $page = 0; } else { $page = $FORM{'page'}; } $page_end = $page + $def - 1; if ($page_end > $#lines) { $page_end = $#lines; } print "

\n"; print "\n"; foreach ($page .. $page_end) { ($number,$pwd,$name,$email,$host,$date,$subject,$value,$url,$how,$ufile,$ctype) = split(/\t/,$lines[$_],12); $ctype =~ s/\n//; $ufile =~ s/\n//; print "

\n"; print "
$subject
\n"; print "投稿日 $date "; if ($email) { print "投稿者 $name "; } else { print "投稿者 $name "; } print "削除

\n"; print "

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

\n"; } else { print "

$value

\n"; } if ($url) { print "$url

\n"; } if ($ufile ne '' && -e "$tmp$ufile") { if ($ctype =~ /^image/i) { print "\"$ufile\"

\n"; } else { if ($ctype ne '') { $ctype = "($ctype)"; } print "$ufile$ctype

\n"; } } print "

\n"; } #--- 改ページ処理 ------------------------------------# $page_next = $page_end + 1; $i = $page + 1; $j = $page_end + 1; print "

\n"; print "\n"; if ($#lines >= 0) { if ($page_end ne $#lines) { print "\n"; print "\n"; print "\n"; print "\n"; } else { print "\n"; } } print "\n"; print "\n"; print "\n"; print "
マスターキー "; print "新着順 $i \- $j
新着順 $i \-> 最後最大記録件数 $max[更新][終了]

\n\n"; print "$msg_btm1\n"; print "$msg_btm2\n"; print "$msg_btm3

\n"; # このスクリプトの著作権表示(かならず表示してください) print "


MiniBBS v8.92t
\n"; print "


\n"; print "

管理者用

\n"; print "\n"; if ((-s $sus) > 1) { print "\n"; print ""; print "\n"; } print "\n"; print ""; print "\n"; print "
"; print "

\n\n"; print "\n"; } sub regist { if ($ref_axs) { $ref = $ENV{'HTTP_REFERER'}; $ref =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg; $ref_url = $reload; if (!($ref =~ /$ref_url/i)) { &error('利用不可',"「$reload」以外からの投稿は受け付けられません."); } } if ($FORM{'name'} eq '') { &error('入力ミス','投稿者を記入してください.'); } $FORM{'name'} =~ s//>/g; if ($FORM{'email'} ne '' && !($FORM{'email'} =~ /\b[-\w.]+@[-\w.]+\.[-\w]+\b/)) { &error('入力ミス','メールアドレスの形式が間違っています.'); } $FORM{'email'} =~ s//>/g; if ($FORM{'subject'} eq '' && $FORM{'value'} eq '') { $COOKIE{'name'} = ''; $COOKIE{'email'} = ''; $COOKIE{'pwd'} = ''; &cookie; print "Set-Cookie: $reload=\n"; &html; exit; } if ($FORM{'subject'} eq '') { &error('入力ミス','題名を記入してください.'); } $FORM{'subject'} =~ s//>/g; if ($FORM{'value'} eq '') { &error('入力ミス','内容を記入してください.'); } if ($max_size) { $value_size = length($FORM{'value'}); if ($value_size > $max_size) { &error('入力ミス',"最大記録サイズ$max_sizeを超えています. 現在$value_sizeサイズです."); } } if ($FORM{'url'} ne '' && !($FORM{'url'} =~ m#(.*)tp(.*)://#)) { &error('入力ミス','URLを正しく記入してください.'); } if ($FORM{'url'} =~ m#^(.*)tp(.*)://$#) { $FORM{'url'} = ''; } if ($fname ne '') { if (!open(UU,"> $tmp$fname")) { &error('エラー','テンポラリーディレクトリが無いかパーミッションが間違っています.'); } print UU $in{'file'}; close(UU); } &cookie; $cook="name\:$FORM{'name'}\,email\:$FORM{'email'}\,pwd\:$FORM{'pwd'}"; print "Set-Cookie: $reload=$cook; expires=$date_gmt\n"; #-- リムネット専用ホスト取得ルーチンの入れ替え位置 : ここから --# $host = $ENV{'REMOTE_HOST'}; $addr = $ENV{'REMOTE_ADDR'}; if ($host eq $addr) { $host = gethostbyaddr(pack('C4',split(/\./,$host)),2) || $addr; } #-- リムネット専用ホスト取得ルーチンの入れ替え位置 : ここまで --# &encode2($FORM{'pwd'}); if (!open(DB,">>$sus")) { &error('設定ミス',"$sus へ書き込みできません. パーミッション等を確認してください."); } print DB "$date_num\t$password\t$FORM{'name'}\t$FORM{'email'}\t$host\t$date_now\t$FORM{'subject'}\t$FORM{'value'}\t$FORM{'url'}\t$FORM{'how'}\t$FORM{'file'}\t$FORM{'ctype'}\n"; close(DB); &error('受付ました',"ただ今投稿された内容は直ちには公開されません.
\n内容を審査の上、管理者によって正式公開しますので暫くお待ちください."); } sub cookie { $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); } sub error { print "Content-type: text/html\n\n"; print "$title\n"; print "\n"; print "$body\n"; print "

$_[0]

\n"; print "

$_[1]

\n"; print "ブラウザの[戻る]ボタンを押して前の画面に移動してください.

\n"; print "\n"; exit; } sub remove { $target = join('|',@RM); foreach $line (@lines) { ($number,$pwd,$name,$email,$host,$date,$subject,$value,$url,$how,$ufile,$ctype) = split(/\t/,$line,12); $ctype =~ s/\n//; $ufile =~ s/\n//; if ($number =~ /$target/) { if (!$admin && $pwd eq '') { &error('削除エラー','都合により管理者にしか削除できない記事が存在しています.'); } if (!$admin && crypt($FORM{'pwd'}, substr($pwd,$salt,2)) ne $pwd) { push(@NEW,$line); } elsif (-e "$tmp$ufile") { unlink "$tmp$ufile"; } } else { push(@NEW,$line); } } if (!open(DB,">$file")) { &error('設定ミス',"$file へ書き込みできません. パーミッションを確認してください."); } print DB "MiniBBSv8t\:$password\n"; print DB @NEW; close(DB); @lines = @NEW; } sub password { print "Content-type: text/html\n\n"; print "$title\n"; print "$body\n"; print "

マスターキーの設定/変更

\n"; if ($first && $message eq '') { print "記事を削除するための管理パスワードを登録します。

\n"; } else { print "$message

\n"; } print "

\n"; print "\n"; if (!$first) { print "旧パスワード
\n"; } print "新パスワード
\n"; print "新パスワード (確認のためもう一度)

\n"; print "

\n"; print "\n"; } sub encode { if ($header eq 'MiniBBSv8t') { if (!$admin) { $message = "$admin旧パスワードが認証されませんでした."; &password; exit; }} else { if (!open(DB,"$file")) { &error('設定ミス',"$file が見つかりません. 設定を確認してください."); } @lines = ; close(DB); } if ($FORM{'pwd1'} =~ /\W/ || $FORM{'pwd1'} eq '') { $message = '新パスワードに英数字以外の文字が含まれているか空欄です.'; &password; exit; } if ($FORM{'pwd1'} ne $FORM{'pwd2'}) { $message = '確認のために入力された新パスワードが一致しません.'; &password; exit; } &encode2($FORM{'pwd1'}); if (!open(DB,">$file")) { &error('設定ミス',"$file へ書き込みできません. パーミッションを確認してください."); } print DB "MiniBBSv8t\:$pwd\n"; print DB @lines; close(DB); &html; exit; } sub encode2 { $now = time; ($p1, $p2) = unpack("C2", $now); $wk = $now / (60*60*24*7) + $p1 + $p2 - 8; @saltset = ('a'..'z','A'..'Z','0'..'9','.','/'); $nsalt = $saltset[$wk % 64] . $saltset[$now % 64]; if (!eval '$pwd = crypt($_[0], $nsalt);') { &error('エラー','暗号処理コマンドが使えませんので設置できません.'); } } sub sus_list { if (!open(DB,"$sus")) { &error('設定ミス',"$sus が見つかりません. 設定を確認してください."); } @SUS = ; close(DB); @SUS = reverse(@SUS); print "Content-type: text/html\n\n"; print "$title\n"; print "$body\n"; print "

公開待ち投稿一覧

\n"; print "実行ボタンを押すと、選択(チェック)された記事が公開されます.
\n"; print "ここで選択しなかった記事は削除されます.

\n"; print "

\n"; print "\n"; print "\n"; foreach (@SUS) { ($number,$pwd,$name,$email,$host,$date,$subject,$value,$url,$how,$ufile,$ctype) = split(/\t/,$_,12); $ctype =~ s/\n//; $ufile =~ s/\n//; print "

\n"; print "
"; print " "; print "$subject
\n"; print "投稿日 $date "; if ($email) { print "投稿者 $name [$host] "; } else { print "投稿者 $name [$host] "; } print "

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

\n"; } else { print "

$value

\n"; } if ($url) { print "$url

\n"; } if ($ufile ne '' && -e "$tmp$ufile") { if ($ctype =~ /^image/i) { print "\"$ufile\"

\n"; } else { if ($ctype ne '') { $ctype = "($ctype)"; } print "$ufile$ctype

\n"; } } print "

\n"; } print "


\n"; print "\n"; } sub move { $target = join('|',@RM); if (!open(DB,"$sus")) { &error('設定ミス',"$sus が見つかりません. 設定を確認してください."); } @SUS = ; close(DB); @OK = grep(/^($target)\t/,@SUS); @NG = grep(!/^($target)\t/,@SUS); foreach $in (@OK) { if ($max <= $#lines + 1) { $line = shift(@lines); ($number,$pwd,$name,$email,$host,$date,$subject,$value,$url,$how,$ufile,$ctype) = split(/\t/,$line,12); $ctype =~ s/\n//; $ufile =~ s/\n//; if (-e "$tmp$ufile") { unlink "$tmp$ufile"; } } push(@lines,$in); } foreach $in (@NG) { ($number,$pwd,$name,$email,$host,$date,$subject,$value,$url,$how,$ufile,$ctype) = split(/\t/,$in,12); $ctype =~ s/\n//; $ufile =~ s/\n//; if (-e "$tmp$ufile") { unlink "$tmp$ufile"; } } if (!open(DB,">$file")) { &error('設定ミス',"$file へ書き込みできません. パーミッションを確認してください."); } print DB "MiniBBSv8t\:$password\n"; print DB @lines; close(DB); if (!open(DB,">$sus")) { &error('設定ミス',"$sus へ書き込みできません. パーミッション等を確認してください."); } print DB ''; close(DB); }