#! /usr/local/bin/perl
#
# ↑この値は設置するサーバに合わせて編集してください。
# 分からない場合はサーバ管理者(プロバイダ等)にお尋ねください。
#_______________________________________________________________________________...
#
# MiniBBS-C Version 1.01
# 簡易BBS クッキー入出力管理版
# (c)2000 rescue.ne.jp
#_______________________________________________________________________________...
#
# <履歴> -HISTORY-
# 2000/08/15 v1.00 初回公開
# 2000/11/09 v1.01 ログインしていないのにメール送信できる症状を改善
# <設置構成例>
#
# § < >内はパーミッション値の例。設置するサーバで最適な値を設定してください。
#
# /CGIが有効な任意のディレクトリ/ <755>
# |
# |-- /data/ <777> データ保存用ディレクトリ(※2)(※5)
# |
# |-- jcode.pl <644> 日本語コード変換ライブラリ(※4)
# |-- login.cgi <666> ユーザ登録データファイル(※3)(※5)(※6)
# |-- minibbs.cgi <755> 実行プログラム(※1) このファイル
#
#
# ※5 このファイルやこのディレクトリ内に作成されるファイルの拡張子を .cgi に設定しておくと、
# Web上から直接閲覧されることを防止できます。これらはCGIプログラムではありませんが、
# この拡張子を利用することで、CGIプログラムを装うことになり、サーバエラーを誘発させるものです。
# ※6 空のファイル(サイズが0バイトの空っぽの新規ファイル)をご自身で用意ください。
#
# <ユーザ登録データファイルの仕様>
#
# ここには登録されたIDが1件1行で記録されます。もし忘れた方から照会があれば、
# このファイルから探すことになります。削除する場合は、直接このファイルを取りだし、
# 対象の1行を削除してください。このファイルはプログラムと同じ転送モード(アスキーモード)
# で扱ってください。なお、値に半角カンマ(,)を始め、特にニックネームには ,:;<> は使えません。
# ログインIDは直接変更することはできますが、このファイル内でユニーク(他に同じものがないこと)で
# なければなりません。
#
# ログインID,ニックネーム,Eメールアドレス<改行>
#
#_______________________________________________________________________________...
#
# 各種設定
#_______________________________________________________________________________...
# このファイルの名前
$bbs = "minibbs.cgi"; #(※1)
# 画面の色や文字色などを設定する
タグの設定
$body = '';
# データ保存用ディレクトリの場所とディレクトリ名の指定 ( /で閉じるのを忘れずに)
$base_dir = "./data/"; #(※2)
# ユーザ登録データファイルの場所とファイル名の指定
$login_file = "./login.cgi"; #(※3)
# データファイルに付ける拡張子 (※5を参照のこと)
# 運用中途での変更はできない
$ext = "cgi";
#終了先のページのURL
$bye = 'http://www.rescue.ne.jp/cgi/minibbs-c/';
# 管理者として登録するユーザのログインID
# このIDでログインした方が任意のファイルの削除権を持つことになる
# 最初は設定せず、設置完了したら実際にログインIDを取得してから設定する
$admin_id = '45Soxbnt9NIe&r7eq76ZGL8Tq83z$#';
# 画面のセンタリングを 1:する 0:しない
$center = 0;
# 1画面に表示する件数
$page = 10;
# 記事データの最大保存件数
# これを超えると古い記事データから削除される
$max_log_size = 100;
# 投稿者チェックを 1:行う 0:行わない
# 行う場合は新規に発行するログインIDを記入したEメール宛てに送信する
# 行わない場合はその場で画面上に表示してEメールが本人のものかを確認しない
$secure = 0;
# 電子メールを送信する外部プログラムの場所と名称の設定
$sendmail = '/usr/sbin/sendmail';
# 管理者のEメールアドレス
$admin = 'rescue@rescue.ne.jp';
# 投稿される記事の題名の色
$subject_color = "#ff8888";
# 投稿される記事の題名の頭に付加する文字や記号
$subject_point = '■';
# 投稿される記事の投稿日や投稿者などの情報の表示色
$info_color = "#ffaaaa";
# 日本語コード変換ライブラリの場所と名称の設定
require './jcode.pl';
# ブラウザのタイトルバーに表示される文字列
$title_bar = '電子掲示板';
# 画面の上部に表示されるメッセージ(HTML)
# $head = <<'EOF'; の次の行から EOF の直前までに記述する
$head = <<'EOF';
電子掲示板
EOF
# 画面の下部に表示されるメッセージ(HTML)
# $end = <<'EOF'; の次の行から EOF の直前までに記述する
$end = <<'EOF';
- 投稿したい方は登録が必要です.
- ログインしている状態で投稿が可能です.
- 同じログインIDで投稿した記事の削除が可能です.
- ログインIDはパスワードと同じで、誰にも教えてはいけません.
|
EOF
# 電子メールされるメール内容の下部に記述されるサイン
# $Sign = <<'EOF'; の次の行から EOF の直前までに記述する
$Sign = <<'EOF';
-------------------------------------------------------
○△□のホームページ http://somewhere.host/~hogehoge/
hogehoge@somewhere.host
EOF
#_______________________________________________________________________________...
if ($jcode'version < 2) { &error('jcode.pl','バージョン2以降のjcode.plを設置してください.',"現在使用中のjcode.plはバージョン$jcode'versionです."); }
($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);
@wday_array = ('日','月','火','水','木','金','土');
$date_now = sprintf("%04d/%01d/%01d (%s) %02d:%02d",$year +1900,$mon +1,$mday,$wday_array[$wday],$hour,$min);
$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; }
&read_cookie($ENV{'SCRIPT_NAME'});
if ($COOKIE{'login'} ne "") { $LOGIN = $COOKIE{'login'}; }
else { $LOGIN = ""; }
&read_data();
if ($in{'mailto'} ne "") { &mail; }
if ($in{'action'} eq "login") { &login_check(); }
elsif ($in{'action'} eq "regist_form") { ®ist_form; }
elsif ($in{'action'} eq "regist") { ®ist; }
elsif ($in{'action'} eq "logoff") { &logoff; }
elsif ($in{'action'} eq "write") { &write_form; }
elsif ($in{'action'} eq "delete" && $in{'delete'} ne "") { &delete; }
elsif ($in{'action'} eq "mail") { &mail; }
&get_dir();
&search();
&view_list();
#___________________________________________________________________..
sub read_data
{
read(STDIN,$buffer,$ENV{'CONTENT_LENGTH'});
if ($ENV{'QUERY_STRING'} ne '') { $buffer .= "\&$ENV{'QUERY_STRING'}"; }
@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); # 半角カナ→全角カナ変換
&jcode'convert(*val,'sjis'); # SJIS変換
$val =~ s/\t//g; # タブ無効
$val =~ s/\r\n/\n/g; # Win→Unix
$val =~ s/\r/\n/g; # Mac→Unix
$val =~ s/</g; # タグ禁止
$val =~ s/>/>/g;
if ($key =~ /;(\d+);/) { $in{'delete'} = $1; }
else { $in{$key} = $val; }
}
}
#___________________________________________________________________..
sub get_dir
{
if (!-e $base_dir) { &error("ERROR","データフォルダが見つかりません."); }
$check = (eval { opendir(DIR,$base_dir); }, $@ eq "");
if (!$check) { &error("ERROR","ファイル取得に失敗しました."); }
@BASE = ();
@list = readdir(DIR);
foreach $file (@list) {
next if -d $file;
if ($file =~ /^(\d+)\.$ext$/) { push(@BASE,$1); }
}
close(DIR);
@BASE = sort { $b <=> $a; } @BASE; # 新しい順にソート
}
#___________________________________________________________________..
sub search
{
$hit = 0;
$next_num = '';
if ($in{'ff'} eq '') { $FF = 0; } else { $FF = $in{'ff'}; }
$TO = $FF + $page - 1;
if ($TO > $#BASE) { $TO = $#BASE; }
foreach $num ($FF .. $#BASE) {
$number = $BASE[$num];
if ($in{'ff'} ne '') { # 2ページ目以降
$allhits = $in{'allhits'};
if ($hit == $page) { last; }
else { push(@NEW,$number); $hit++; }
}
else { # 1ページ目の処理
if ($allhits % $page == 0) { push(@Index,$num); }
if ($hit < $page) { $hit++; push(@NEW,$number); }
$allhits++;
}
}
if ($in{'allhits'} eq '') {
push(@Buf,"allhits=$allhits");
$in{'allhits'} = $allhits;
}
if ($in{'ff'} ne '') { @Index = split(/\s/,$in{'idx'}); }
$count_new = @NEW;
}
#___________________________________________________________________..
sub view_list
{
print &html_headder;
if ($center) { print "\n"; }
print << "EOF";
$head
EOF
if ($LOGIN eq "") {
print << "EOF";
EOF
}
else {
if ($LOGIN eq $admin_id) { $kanrisha = "管理者として"; }
print << "EOF";
EOF
}
if (!@NEW) { print "○記事はありません.\n"; }
else {
print <<"EOF";
全$in{'allhits'}件 <更新>
ページ移動 ⇒ [
EOF
$buf = join('&',@Buf);
$idx = join('+',@Index);
$n = 0;
foreach (0 .. $#Index) {
$view_page = $_ + 1;
if ($FF == $Index[$_] || ($in{'ff'} eq '' && $_ == 0)) { print " ↓"; $page_now = $view_page; $n = 1; }
elsif ($view_page == 1) { print " $view_page"; }
else {
if ($n) { $next = "次"; $n = 0; } else { $next = $view_page; }
print " $next";
}
}
$FROM = $page_now * $page - ($page - 1);
$LAST = $FROM + $count_new - 1;
print " 終了]\n";
if ($center) { print "
\n"; }
print <<"EOF";
PAGE $page_now ($FROM〜$LAST)
\n";
print "$end\n";
# 次の行は削除してはいけません.
print "\n";
print &html_footer;
exit;
}
#___________________________________________________________________..
sub delete
{
undef %FILE;
$head_end = 0;
if (open(IN,"$base_dir$in{'delete'}\.$ext")) {
while () {
if ($head_end) { $FILE{'honbun'} .= $_; next; }
elsif (/^$/) { $head_end = 1; }
s/\n//g;
($key,$val) = split(/\t/);
$FILE{$key} .= "\0" if (defined($FILE{$key}));
$FILE{$key} .= $val;
}
close(IN);
}
else { &error("削除できませんでした","$!\.","データを開くことができませんでしたので、既に削除されている可能\性があります."); }
if ($LOGIN eq "") { &error("ログインしてください","現在このブラウザには誰もログインしていません.","削除したい記事を投稿した際に利用したIDでログインが必要です."); }
if ($LOGIN ne $FILE{'id'} && $LOGIN ne $admin_id) { &error("削除できませんでした","現在ログインされているのは $COOKIE{'uname'} さんです.","現在ログインしているIDで投稿された記事以外の削除は、管理者以外はできません."); }
$result = unlink("$base_dir$in{'delete'}\.$ext");
if (!$result) { &error("削除できませんでした","管理者に、$in{'delete'}番記事の削除を依頼してください."); }
}
#___________________________________________________________________..
sub login_check
{
if ($LOGIN ne "") { &error("現在ログイン中です","現在ログインされているのは $COOKIE{'uname'} さんです.","別のIDでログインする場合は一旦ログオフしてください."); }
if (!open(IN,$login_file)) { &error("ERROR","ログインデータファイルが見つかりません."); }
while () {
s/\n//g;
@CSV = ();
($ID,$UNAME,$EMAIL) = split(/\,/,$_,3);
if ($in{'loginid'} eq $ID) { $LOGIN = $ID; last; }
}
close(IN);
if ($LOGIN eq "") { ®ist_form("記入されたログインIDは認証されませんでした."); }
&get_cookie_expires;
print "Set-Cookie: $ENV{'SCRIPT_NAME'}=login:$ID\,uname:$UNAME\,email:$EMAIL; expires=$date_gmt\n";
$COOKIE{'login'} = $LOGIN;
$COOKIE{'uname'} = $UNAME;
$COOKIE{'email'} = $EMAIL;
}
#___________________________________________________________________..
sub logoff
{
$LOGIN = $COOKIE{'login'} = $COOKIE{'uname'} = $COOKIE{'email'} = "";
&get_cookie_expires;
print "Set-Cookie: $ENV{'SCRIPT_NAME'}=; expires=Sun, 01-Jan-1995 01:00:00 GMT\n";
}
#___________________________________________________________________..
sub write_form
{
if ($ENV{'QUERY_STRING'} ne '') { &error("ERROR","不正なアクセスを検知しました."); }
if ($in{'subject'} eq '') { &error("未入力があります","タイトルを入力してください."); }
if ($in{'honbun'} eq '') { &error("未入力があります","本文を入力してください."); }
$in{'honbun'} = &tag($in{'honbun'});
$number = sprintf("%04d%02d%02d%02d%02d%02d",$year +1900,$mon +1,$mday,$hour,$min,$sec);
$agent = $ENV{'HTTP_USER_AGENT'};
$agent =~ s/</g;
$agent =~ s/>/>/g;
if (open(DATA,"> $base_dir$number\.$ext")) {
print DATA "id\t$COOKIE{'login'}\n";
print DATA "date\t$date_now\n";
print DATA "uname\t$COOKIE{'uname'}\n";
print DATA "email\t$COOKIE{'email'}\n";
print DATA "addr\t$addr\n";
print DATA "host\t$host\n";
print DATA "agent\t$agent\n";
print DATA "subject\t$in{'subject'}\n";
print DATA "\n";
print DATA "$in{'honbun'}\n";
close(DATA);
chmod(0666,"$base_dir$number\.$ext");
}
else { &error("ERROR","$!\.","投稿データを記録できる状態になっていません."); }
&get_dir();
$d = 0;
foreach $file (@BASE) {
$d++;
if ($d <= $max_log_size) { next; }
$deleted = unlink("$base_dir$file\.$ext");
}
if ($deleted) { &get_dir(); }
&search();
&view_list();
}
#___________________________________________________________________..
sub sendmail
{
local(@VALUE) = @_; # To From Reply-To Subject Value Sign
if ($VALUE[0] !~ /\b[-\w.]+@[-\w.]+\.[-\w]+\b/) { &error("ERROR","送信先アドレスの設定に誤りがありましたので、送信できませんでした."); }
if (!open(OUT,"| $sendmail -t")) { return; }
print OUT &jis("X-Processed: $date_now\n");
print OUT "X-HTTP-REFERER: $ENV{'HTTP_REFERER'}\n";
print OUT "X-HOST-ADDR: $host \[$addr\]\n";
print OUT "To: $VALUE[0]\n";
print OUT "From: $VALUE[1]\n";
print OUT "Reply-To: $VALUE[2]\n";
print OUT &jis("Subject: $VALUE[3]\n");
print OUT "Content-Transfer-Encoding: 7bit\n";
print OUT 'Content-Type: text/plain; charset=iso-2022-jp' . "\n\n";
print OUT &jis($VALUE[4]);
print OUT "\n\n";
print OUT &jis($VALUE[5]);
print OUT "\n";
close(OUT);
}
#___________________________________________________________________..
sub regist
{
local($id,$line);
if ($ENV{'QUERY_STRING'} ne '') { &error("ERROR","不正なアクセスを検知しました."); }
if ($LOGIN ne "") { &error("現在ログイン中です","現在ログインされているのは $COOKIE{'uname'} さんです.","別のIDを発行する場合は一旦ログオフしてください."); }
if ($in{'uname'} eq "") { &error("未入力があります","名前またはニックネームを入力してください."); }
if ($in{'uname'} =~ /[,:;<>]/) { &error("入力エラーが発生しました","名前またはニックネームには、カンマ、コロン、セミコロン、< > はご利用になれません."); }
if ($in{'email'} !~ /\b[-\w.]+@[-\w.]+\.[-\w]+\b/) { &error("未入力があります","Eメールを入力する場合は半角で正しく記入してください."); }
@char = ('0'..'9','a'..'z','A'..'Z','$','#','%','&');
srand(time|$$);
foreach (1..30) {
{
local(@temp);
push(@temp,splice(@char,rand(@char),1)) while @char;
@char = @temp;
}
$id = $char[($_)] . $id;
}
if (!open(IN,$login_file)) { &error("ERROR","ログインデータファイルが見つかりません."); }
while () {
s/\n//g;
@CSV = ();
($ID,$UNAME,$EMAIL) = split(/\,/,$_,3);
if ($ID eq $id) { &error("再試行","IDの発行に失敗しました.","戻ってもう一度ボタンを押してください."); }
}
close(IN);
$line = "$id\,$in{'uname'}\,$in{'email'}";
if (!open(OUT,">> $login_file")) { &error("ERROR","$!\.","ログインデータファイルが見つからないか、記録できる状態になっていません."); }
print OUT "$line\n";
close(OUT);
if ($secure) {
# To From Reply-To Subject Value Sign
&sendmail($in{'email'},$admin,$admin,'ログインIDのおしらせ',"$in{'uname'}さんへ\n\nログインIDは $id です.\n投稿時にはこのIDでログインすると投稿画面が表\示されます.\n投稿時にはログインした方の名前(またはニックネーム)とEメール(公開されませんがメールを受け取れます)が使われます.\nご利用のブラウザをあなた以外の方が利用することがある場合は、利用終了したらログオフすることを心がけてください.",$Sign);
&error("送信しました","投稿に必要なログインIDは$in{'email'}宛てに送信しました.","届かない場合は$adminまでご照会ください.");
}
&get_cookie_expires;
print "Set-Cookie: $ENV{'SCRIPT_NAME'}=login:$id\,uname:$in{'uname'}\,email:$in{'email'}; expires=$date_gmt\n";
if (!$secure) {
&sendmail($in{'email'},$admin,$admin,'ログインIDのおしらせ',"$in{'uname'}さんへ\n\nログインIDは $id です.\n投稿時にはこのIDでログインすると投稿画面が表\示されます.\n投稿時にはログインした方の名前(またはニックネーム)とEメール(公開されませんがメールを受け取れます)が使われます.\nご利用のブラウザをあなた以外の方が利用することがある場合は、利用終了したらログオフすることを心がけてください.",$Sign);
&error("登録しました","投稿に必要なログインIDは $id です.","ただ今ご記入した電子メール宛てにも送信しましたので、メモ代わりに保存してください.","投稿時にはこのIDでログインすると投稿画面が表\示されます.","投稿時にはログインした方の名前(またはニックネーム)とEメール(公開されませんがメールを受け取れます)が使われます.","このブラウザをあなた以外の方が利用することがある場合は、利用終了したらログオフすることを心がけてください.");
}
}
#___________________________________________________________________..
sub get_cookie_expires
{
($secg,$ming,$hourg,$mdayg,$mong,$yearg,$wdayg,$ydayg,$isdstg) = gmtime(time + 90*24*60*60);
$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 regist_form
{
local($message) = @_;
print &html_headder;
if ($center) { print "\n"; }
print << "EOF";
EOF
if ($center) { print "\n"; }
print &html_footer;
exit;
}
#___________________________________________________________________..
sub mail
{
if (!$LOGIN) { &error("送信できません","ログインしてください."); }
undef %FILE;
$head_end = 0;
if (open(IN,"$base_dir$in{'mailto'}\.$ext")) {
while () {
if ($head_end) { $FILE{'honbun'} .= $_; next; }
elsif (/^$/) { $head_end = 1; }
s/\n//g;
($key,$val) = split(/\t/);
$FILE{$key} .= "\0" if (defined($FILE{$key}));
$FILE{$key} .= $val;
}
close(IN);
}
else { &error("送信できません","メール送信する相手の方が投稿された記事は削除されたか、読み取りできませんでした."); }
if ($in{'action'} eq "") {
@VALs = split(/\n/,$FILE{'honbun'});
foreach $mail_value (@VALs) {
$mail_value =~ s/<//g;
$mail_value =~ s/<[^<>]*>//g;
$mail_value =~ s/</g;
$mail_value =~ s/>/>/g;
$QVALUE .= "$FILE{'uname'}> $mail_value\n";
}
$QVALUE .= "\n";
@line_count = split(/\n/,$QVALUE);
$line_count = @line_count;
print &html_headder;
if ($center) { print "\n"; }
print <<"EOF";
$FILE{'uname'}さんへの電子メール
記事番号$in{'mailto'}を投稿した$FILE{'uname'}さんへメールを転送します。
なお、確実に相手に配信することを確約するものではありません。
|
EOF
if ($center) { print "\n"; }
print &html_footer;
exit;
}
elsif ($in{'action'} eq "mail") {
# To From Reply-To Subject Value Sign
&sendmail($FILE{'email'},$admin,$in{'email'},$in{'title'},"《転送メールのご案内》\n\nこのメールは電子掲示板(記事番号$in{'mailto'})から送信されたものです。\n\n----- ここから -----\n\n$in{'Value'}\n\n----- ここまで -----",$Sign);
&error("送信しました","相手に確実に配信するものを確約するものではありません.","その記事に記録された投稿者のEメールは必ずしも現在存在するものとは限りません.");
}
}
#___________________________________________________________________..
sub read_cookie
{
local($name) = @_;
$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 $name) {
@pairs = split(/,/,$val);
foreach $pair (@pairs) {
($key,$val) = split(/:/,$pair,2);
$COOKIE{$key} = $val;
}
last;
}
}
}
#___________________________________________________________________..
sub tag
{
local($_) = $_[0];
s/\n/\f/g;
s/"/"/g;
#の許可
1 while s/(.*)(<(b)>(.*)<\/b>)/$1$4<\/b>/i;
1 while s/(.*)(<(i)>(.*)<\/i>)/$1$4<\/i>/i;
1 while s/(.*)(<(u)>(.*)<\/u>)/$1$4<\/u>/i;
s/\f/\n/g;
$_;
}
#___________________________________________________________________..
sub error
{
local (@msg) = @_;
local ($i);
print &html_headder;
if ($center) { print "\n"; }
print << "EOF";
$msg[0]
EOF
foreach $i (1 .. $#msg) { print "- $msg[$i]\n"; }
print <<"EOF";
|
EOF
if ($center) { print "\n"; }
print &html_footer;
exit;
}
#___________________________________________________________________..
sub html_headder
{
local($i);
$i = << "EOF";
Content-type: text/html\n
$title_bar
$body
EOF
return $i;
}
#___________________________________________________________________..
sub html_footer
{
local($i);
$i = << "EOF";
EOF
return $i;
}
#___________________________________________________________________..
sub jis
{
local($msg) = @_;
$msg =~ s/<//g;
&jcode'convert(*msg,'jis');
return $msg;
}