#! /usr/bin/perl # # ↑このパスを環境に合わせて変更してください。 ##################################################################### # # レトロな簡易BBS v1.20 # for PC,iMODE,ezWEB,J-SKY (注) # # (c)2004 by CGI RESCUE # http://www.rescue.ne.jp/ # # (注) 携帯の機種によっては取り扱いできない場合があります。 # # [履歴] # 2004/JUN/09 v1.00 リリース # 2004/AUG/31 v1.01 セキュリティ上のバグの修正 # 2007/MAR/24 v1.10 日本語を含まない投稿を規制する設定を追加 # 2007/MAY/25 v1.20 言語の最優先順位が日本語でない場合を規制する設定を追加 # # [設置例] < >内はパーミッション値(相当値) # # bbs/ # |-- data.cgi <666> ... 空のファイルをご用意ください # |-- jcode.pl <644> ... v2.0以降のものを使ってください # |-- minibbs.cgi <755> ... このファイル # ##################################################################### #-------------------------------------------------------------------- # ▽設置アドレス $location = 'http://設置したアドレス/minibbs.cgi'; #-------------------------------------------------------------------- # ▽管理者のメールアドレス # 投稿削除は管理者のみ行えますので、必ず設定してください。 $admin_email = 'メールアドレス'; #-------------------------------------------------------------------- # ▽ブラウザの上部バーに表示するタイトルです。 # お気に入りにする場合の名前にもなります。 $bar_title = '掲示板'; #-------------------------------------------------------------------- # ▽文字、背景、リンクなどの色を設定するタグです。 # タグの詳細な設定方法は参考書などをご覧ください。 $body = ''; #-------------------------------------------------------------------- # ▽掲示板上部に表示するメッセージです。 # $head = <<'EOF'; の下から EOF の上までの間にHTML書式で記述します。 $head = <<'EOF';

掲示板

EOF #-------------------------------------------------------------------- # ▽【携帯用】掲示板上部に表示するメッセージです。 # $head = <<'EOF'; の下から EOF の上までの間にHTML書式で記述します。 $head2 = <<'EOF'; 掲示板
EOF #-------------------------------------------------------------------- # ▽掲示板から抜ける先のリンクを記入します。 # 掲示板上では "HOME" という部分にリンクされます。 $home = 'http://あなたのホームページなど'; #-------------------------------------------------------------------- # ▽【携帯用】掲示板から抜ける先のリンクを記入します。 # 掲示板上では "QUIT" という部分にリンクされます。 # 携帯向けページが無い場合は $home2 = ''; と設定してください。 $home2 = 'http://あなたの携帯用ホームページなど'; #-------------------------------------------------------------------- # ▽削除パスワードの設定です。 # このパスワードにより投稿の削除が可能になります。 # ' 'の間に半角英数字で指定してください。 $admin = '00000000'; #-------------------------------------------------------------------- # ▽記録ログ数の上限の設定です。 # 投稿がこの件数を超えたら、古いものから自動削除されていきます。 # 推奨値は 100 です。過度に大きな数字を設定しないでください。 $max = 100; #-------------------------------------------------------------------- # ▽1画面に一覧する投稿の数です。 # この数ずつページを変えることができます。(携帯は10に固定) $page = 10; #-------------------------------------------------------------------- # ▽投稿の色設定です。 # $bodyColor は本文の色、$nameColor は名前と日付の色、$titleColor は題名の色です。 # ' 'の間に、#RGBまたはHTMLで扱える色名で指定してください。 $bodyColor = '#335588'; $nameColor = '#996699'; $titleColor = '#000000'; #-------------------------------------------------------------------- # ▽日本語が含まれない投稿を規制する設定です。 # 規制する場合は 1 を、しない場合は 0 を設定してください。 $spam1 = 1; #-------------------------------------------------------------------- # ▽言語の最優先順位が日本語でない場合を規制する設定です。 # 規制する場合は 1 を、しない場合は 0 を設定してください。 $spam2 = 1; #------------------------------------------------------------------------------------------------- # ここから下はプログラミングが出来る方が必要に応じて編集してご利用ください。 #------------------------------------------------------------------------------------------------- # 日本語コード変換ライブラリ(v2.0以降) require './jcode.pl'; # 時刻設定 ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time); # (秒,分,時,日,月(0〜11),年(年号-1900),曜日(0〜6),1月1日から何日目,サマータイムの有無) @wday_array = ('Sun','Mon','Tue','Wed','Thu','Fri','Sat'); @mon_array = ('Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'); $timeStamp = sprintf("%04d/%s/%01d %s %02d:%02d",$year +1900,$mon_array[$mon],$mday,$wday_array[$wday],$hour,$min); $time = time; # アクセス情報 $addr = $ENV{'REMOTE_ADDR'}; if ($host eq "" || $host eq $addr) { $host = gethostbyaddr(pack('C4',split(/\./,$addr)),2) || $addr; } $via = $ENV{'HTTP_VIA'}; $xfor = $ENV{'HTTP_X_FORWARDED_FOR'}; $for = $ENV{'HTTP_FORWARDED'}; if ($for eq "") { $for = "-"; } $agent = $ENV{'HTTP_USER_AGENT'}; $agent =~ s//)/g; if ($via ne "") { $trueip = $xfor; } else { $trueip = $addr; $via = "-"; } if ($xfor ne "") { $xfor_name = gethostbyaddr(pack('C4',split(/\./,$xfor)),2) || $xfor; $xfor = "\[$xfor\]"; } else { $xfor_name = "-"; } $host_check = "$host\[$addr\] trueip:$trueip via:$via for:$for xfor:$xfor_name$xfor"; if ($ENV{'HTTP_USER_AGENT'} =~ /DoCoMo|ASTEL|PDXGW|J\-PHONE|UP\.Browser/) { $K = 1; $page = 10; } else { $K = 0; } # # データ入力 # if ($ENV{'QUERY_STRING'} ne "") { # クエリー入力 @pairs = split(/&/,$ENV{'QUERY_STRING'}); foreach $pair (@pairs) { ($name,$value) = split(/=/,$pair); $value =~ tr/+/ /; $value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C",hex($1))/eg; $value =~ s/\r//g; $value =~ s/\n//g; $value =~ s/\f//g; $value =~ s/\t//g; $value =~ s/\.//g; $value =~ s/\///g; $cmd{$name} = $value; } $cmd = $cmd{'cmd'}; } else { read(STDIN,$buffer,$ENV{'CONTENT_LENGTH'}); # 標準入力 @pairs = split(/&/,$buffer); foreach $pair (@pairs) { ($name,$value) = split(/=/,$pair); $name =~ tr/+/ /; $name =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C",hex($1))/eg; $value =~ tr/+/ /; $value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C",hex($1))/eg; &jcode'h2z_sjis(*value); &jcode'convert(*value,'sjis'); $value =~ s/\r\n/\r/g; $value =~ s/\n//g; $value =~ s/\f//g; $value =~ s/\t//g; if ($value !~ /&#(\d\d\d\d\d);/) { $value =~ s/&/&/g; } $value =~ s/"/"/g; $value =~ s//>/g; $in{$name} = $value; } $cmd = $in{'cmd'}; } # # 分岐 # if ($admin eq "") { $err = "マスターキーが設定されていません"; } if ($cmd{'read'} ne "") { &TOP(); &DIRECT($cmd{'read'}); &BOTTOM; exit; } if ($cmd eq "add" || $cmd eq "del") { $err = &FILE; if ($K) { &TOP($head2); &LIST; &FORM; &BOTTOM; } else { &TOP($head); &FORM; &LIST; &BOTTOM; } } else { if ($K) { &TOP($head2); &LIST; &FORM; &BOTTOM; } else { &TOP($head); &FORM; &LIST; &BOTTOM; } } # # 出力(上) # sub TOP { local($head) = @_; # エラーメッセージ if ($jcode'version < 2) { $err = "バージョン2以降のjcode.plを設置してください"; } if ($err) { if ($K) { $errmessage = "
†$err
\n"; } else { $errmessage = "
†$err
\n"; } } print <<"EOF"; Content-type: text/html; charset=Shift_JIS\n $bar_title $body $head $errmessage EOF } # # 出力(フォーム) # sub FORM { print <<"EOF";
名前

題名

本文

EOF } # # 読み出し(携帯) # sub DIRECT { local($code) = @_; if (open(F, "./data.cgi")) { @BASE = ; close(F); } else { print $!; return; } @PICKUP = grep(/$code\t/,@BASE); if (!@PICKUP) { print "削除されたか見つかりません"; } else { $PICKUP[0] =~ s/\n//g; $len = length($PICKUP[0]); ($CODE,$DATE,$NAME,$SUBJECT,$BODY,$PWD,$HOST) = split(/\t/,$PICKUP[0],7); $BODY =~ s/\r/
\n/g; print "$SUBJECT
\n"; # 題名 print "$NAME
\n"; # 名前 print "$DATE
\n"; # 日付 print "$BODY
($lenバイト)\n"; # 本文 } } # # 一覧処理 # sub LIST { if (open(F, "./data.cgi")) { @BASE = ; close(F); } else { return; } if ($home2 ne "") { $home2 = "QUIT|"; } else { $home2 = ""; } if (!@BASE) { if ($K) { print "
$home2更新

\n"; } else { print "||| HOME | 更新 |||"; } return; } # ページ処理↓ if ($cmd{'ff'} eq '') { $FF = 0; } else { $FF = $cmd{'ff'}; } $TO = $FF + $page - 1; if ($TO > $#BASE) { $TO = $#BASE; } $hit = 0; foreach $num ($FF .. $#BASE) { $filename = $BASE[$num]; if ($hit < $page) { $hit++; push(@DATA,$filename); } } $allhits = @BASE; $cmd{'allhits'} = $allhits; $count_new = @DATA; if ($cmd{'vp'} eq "") { $cmd{'vp'} = 1; } $n = $vp = 0; for ($jump = $page; $jump < $cmd{'allhits'} + $page; $jump += $page) { $vp++; $last_page++; } $start = $cmd{'vp'} - 4; if ($start < 1) { $start = 1; } $end = $cmd{'vp'} + 4; if ($end >= $last_page) { $end = $last_page; } $ffv = ($last_page - 1) * $page; $n = $vp = 0; foreach $vp ($start .. $end) { if ($vp == $cmd{'vp'}) { $page_now = $vp; last; } } $FROM = $page_now * $page - ($page - 1); $LAST = $FROM + $count_new - 1; if (!$K) { print <<"EOF";

EOF } if ($K) { print "ページ$page_now(全$cmd{'allhits'}件)"; } print <<"EOF";
EOF if ($K) { $i = 0; } foreach (@DATA) { s/\n//g; ($CODE,$DATE,$NAME,$SUBJECT,$BODY,$PWD,$HOST) = split(/\t/,$_,7); if ($K) { $i++; if ($i > 9) { $i = 0; } $len = length($_); print "$i)$SUBJECT($len)
\n"; } else { $BODY =~ s/\r/
\n/g; print "$SUBJECT    "; # 題名 print "$NAME   $DATE  "; # 名前と日付 print "x
\n"; print "
$BODY
\n"; # 本文 print "
\n"; # 仕切り線 } } # ページリンクの生成↓ if ($cmd{'vp'} eq "") { $cmd{'vp'} = 1; } if (!$K) { print "ページ$page_now(全$cmd{'allhits'}件)  [ "; } $start = $cmd{'vp'} - 5; if ($start < 1) { $start = 1; } elsif ($start == 1) { ; } elsif ($start > 2 && !$K) { print "1 .."; } elsif (!$K) { print "1"; } $end = $cmd{'vp'} + 5; if ($end >= $last_page) { $end = $last_page; } elsif ($end < $last_page - 1) { $last_link = " .. $last_page\n"; } else { $last_link = " $last_page\n"; } $n = $vp = 0; foreach $vp ($start .. $end) { $ffv = ($vp - 1) * $page; $title = ''; if ($vp == $cmd{'vp'}) { $n = 1; $page_now = $vp; if (!$K) { print " $vp"; } } elsif ($vp == 1 && !$K) { print "1 "; } else { if ($n) { $n = 0; if (!$K) { $Next_link = "次のページ"; } else { $Next_link = "#)次のページ
\n"; } $next = $vp; } else { $next = $vp; } if (!$K) { print " $next"; } } } if ($K) { print "$Next_link$home2*)更新
\n"; } else { print "$last_link ]  $Next_link  ||| HOME | 更新 | 携帯に送る |||

\n"; } } # # 出力(下) # sub BOTTOM { # ↓ これらのリンクを削除・改ざんすると利用規定違反となります。 if (!$K) { print "

CGI RESCCUE
\n"; } elsif (!$cmd{'read'}) { print "
管理者へメール
CGI RESCUE
\n"; } print "\n"; } # # ファイル操作 # sub FILE { if ($cmd eq "add") { # 投稿 return "名前を入力してください" if $in{'uname'} eq ""; return "名前は全角なら15文字(30バイト)以内で" if length($in{'uname'}) > 30; return "題名を入力してください" if $in{'subject'} eq ""; return "名前は全角なら25文字(50バイト)以内で" if length($in{'subject'}) > 50; return "本文を入力してください" if $in{'body'} eq ""; return "本文は全角なら500文字(1000バイト)以内で" if length($in{'body'}) > 1000; @HTTP_ACCEPT_LANGUAGE = split(/,/,$ENV{'HTTP_ACCEPT_LANGUAGE'}); return "言語の最優先順位が日本語でない場合は投稿できません。" if $HTTP_ACCEPT_LANGUAGE[0] ne 'ja' && $spam2; return "内容文に日本語コードが検知できませんでした。" if &chkZenkaku($in{'body'}) ne 'sjis' && $spam1; sub chkZenkaku { local($line) = @_; (@line) = &jcode'getcode(*line); $line[1]; } ## $pwd = &CRYPT($in{'pwd'}); # 暗号化(今後投稿者削除機能を追加する場合の予定処理) } elsif ($cmd eq "del") { # 削除 return "投稿したときの削除キーを入力してください" if $in{'key'} eq ""; } if (open(F, "+< ./data.cgi")) { flock F, 2; while () { s/\n//g; ($CODE,$DATE,$NAME,$SUBJECT,$BODY,$PWD,$HOST) = split(/\t/,$_,7); if ($cmd eq "del" && $CODE eq $in{'num'}) { # 削除 if ($in{'key'} eq $admin) { $d = "削除しました"; next; } else { close(F); return "削除キーが合いませんでした"; } } push(@BASE,"$_\n"); } eval 'truncate F,0;'; # truncate()が使えないサーバでは致命的なエラーとなる seek(F,0,0); if ($cmd eq "add") { unshift(@BASE,"$time\t$timeStamp\t$in{'uname'}\t$in{'subject'}\t$in{'body'}\t$pwd\t$host_check\n"); # 投稿 if ($#BASE > $max - 1) { $limit = $max - 1; } # $max行まで保存する else { $limit = $#BASE; } foreach $i (0..$limit) { print F $BASE[$i]; } } else { print F @BASE; } close(F); undef %in; if ($cmd eq "del" && $d ne "") { return $d; } else { return 0; } } else { return "'data.cgi'の設置が正しくされていません"; } } # # 暗号化 # sub CRYPT { my $pass = $_[0]; srand(); my @saltset = ('a'..'z','A'..'Z','0'..'9','.','/'); my $nsalt = $saltset[int(rand(64))] . $saltset[int(rand(64))]; return crypt($pass, $nsalt); }