#!/usr/local/bin/perl
#┌─────────────────────────────────
#│ WEB FORUM v3.24 (2002/02/04)
#│ Copyright(C) KENT WEB 2002
#│ webmaster@kent-web.com
#│ http://www.kent-web.com/
#└─────────────────────────────────
$ver = 'Web Forum v3.24';
#┌─────────────────────────────────
#│[ 注意事項 ]
#│ 1. このスクリプトはフリーソフトです。このスクリプトを使用した
#│ いかなる損害に対して作者は一切の責任を負いません。
#│ 2. 設置に関する質問はサポート掲示板にお願いいたします。
#│ 直接メールによる質問は一切お受けいたしておりません。
#└─────────────────────────────────
#
# [設置例] かっこ内はパーミッション
#
# public_html / index.html (ホームページ)
# |
# +-- bbs / wforum.cgi [755]
# | wf_log.cgi [666]
# | wf_admin.cgi [755]
# | jcode.pl [644]
# | fold.pl [644]
# | pastno.dat [666] ... (過去ログ用)
# |
# +-- past [777] / 0001.cgi [666] ... (過去ログ用)
# |
# +-- lock [777] /
#
#============#
# 基本設定 #
#============#
# ライブラリ取込み
require '../jcode.pl';
require '../fold.pl';
# 掲示板タイトル名
$title = "Wonderland Forum";
# タイトルの色
$t_color = "#004080";
# タイトルの大きさ(ポイント数:スタイルシート)
$t_point = '18pt';
# タイトル/本文の文字タイプ
$t_face = 'MS UI Gothic';
# タイトル画像を使用するとき
$t_gif = "../../data/wforum/sow0/title.gif";
$t_wid = 199; # 画像の横サイズ(ピクセル)
$t_hgt = 33; # 画像の縦サイズ(ピクセル)
# 本文の文字大きさ(ポイント数:スタイルシート)
$pt = '10pt';
# 最大記事数
$max = 200;
# 戻り先のURL(index.htmlなど)
$home = "http://www.senseofwonder.org/~hasegawa/sow/wonderland.shtml";
# 壁紙・背景色・文字色など
$bground = "../../images/bg.jpg"; # 壁紙の指定
$bgcolor = "#F1F1F1"; # テキスト色
$text = "#004080"; # 文字色
$link = "#0000E3"; # リンク色(未訪問)
$vlink = "#008080"; # リンク色(既訪問)
$alink = "#DD0000"; # リンク色(訪問中)
# スクリプト名
$script = './wforum.cgi';
# ログファイル名
$logfile = '../../data/wforum/sow6/wf_log.cgi';
# 管理ファイル
$admin = './wf_admin.cgi';
# タグの許可 (0=no 1=yes)
$tagkey = 1;
# ロックファイル機構
# 0 : 行なわない
# 1 : 行なう(symlink関数式)
# 2 : 行なう(mkdir関数式)
$lockkey = 0;
# ロックファイル名
$lockfile = '../../data/wforum/sow6/wforum.lock';
# URL自動リンク (0=no 1=yes)
$autolink = 1;
# 記事の [題名] の色
$sub_color = "#DD0000";
# 記事下地の色(一括表示時等)
$tbl_color = "#FFFFFF";
# 記事にNEWマークを付ける時間
$new_time = 48;
# NEWマークの色
$new_color = "#FF80FF";
# NEWマークの表示形態
# → 画像を使用する場合には $newmark = '';
# というように IMGタグを記述してもよい
$newmark = 'new!';
# 記事NOの色
$no_color = "#800000";
# 新着記事一括表示の記事数
$sortcnt = 20;
# 頁あたりツリー表示数
$p_tree = 10;
# リストに表示する「記事タイトル」の最大長(文字数:半角文字換算)
$sub_length = 40;
# メールアドレスの入力を必須 (0=no 1=yes)
$in_email = 1;
# レスがついたらツリー毎トップへ移動 (0=no 1=yes)
$top_sort = 1;
# レスは下から順に付ける (0=no 1=yes)
$bot_res = 1;
# 引用部色変更
# 1) ここに色指定を行うと「引用部」を色変更します
# 2) 必ずタグは禁止処理にしてください ($tagkey=1;)
# 3) この機能を使用しない場合は何も記述しないで下さい ($refcolor="";)
$refcolor = "#804000";
# methodの形式 (POST/GET)
$method = 'POST';
# 記事の更新は「method=POST」限定(セキュリティ対策)
# 0 : no
# 1 : yes
$MethPost = 1;
# 投稿があるとメール通知する : sendmail必須
# 0 : 通知しない
# 1 : 通知する(自分の記事は送信しない)
# 2 : 通知する(自分の記事も送信する)
$mailing = 2;
# メール通知する際のメールアドレス
$mailto = 'sow@freeml.com';
# sendmailパス(メール通知する時)
$sendmail = '/usr/sbin/sendmail';
# ツリーのヘッダー記号
$treehead = "★";
# 過去ログ機能 (0=no 1=yes)
$pastkey = 1;
# 過去ログカウントファイル
$nofile = '../../data/wforum/sow6/pastno.dat';
# 過去ログのディレクトリ(最後は / で閉じる)
$pastdir = '../../data/wforum/sow6/past/';
# 過去ログ1ページ当りの最大行数
# → これを超えると自動的に次ファイルを生成します
$max_line = '650';
# アクセス制限(ホスト名を記述)
@deny = (
'*.hogehoge.xxx',
'*.proxy.xxx',
'd-osaka.nttpc.ne.jp',
'*.p-osaka.nttpc.ne.jp',
'ap.yournet.ne.jp',
'vectant.ne.jp',
'bbexcite.jp',
'cyberbb.ne.jp',
'fbb.ReSET.JP',
'nodomaintransfer28.com',
'CF059157190021.cims.jp',
'osaknt01.ap.so-net.ne.jp',
'69.46.16.231',
'*.kcn.ne.jp',
'*.osaka-ip.dti.ne.jp',
'*.ppp11.odn.ad.jp',
'p1247-ipbf311osakakita.osaka.ocn.ne.jp',
'*.oska.nt.adsl.ppp.infoweb.ne.jp',
'*.osk.mesh.ad.jp',
'*.s06.a027.ap.plala.or.jp',
'*.ap.highway.ne.jp',
'*.ppp.u-netsurf.ne.jp',
'j111165.ppp.asahi-net.or.jp',
'p2135-ipbf614osakakita.osaka.ocn.ne.jp',
'*osakakita.osaka.ocn.ne.jp',
'bflets-ba-west-2-27.dsn.jp',
'*.ap.kagoya.net',
'153.37.205.61.west.flets.alpha-net.ne.jp',
'*.i-revonet.jp',
'flets-a-west-8-246.dsn.jp',
'*.nt.ftth4.ppp.infoweb.ne.jp',
'internetserviceteam.com',
'bflets-ba-west-2-83.dsn.jp',
'20.net220148188.t-com.ne.jp',
'*.urban.ne.jp',
'163.208.12.61.ap.zero-isp.net',
'');
# 禁止ワード(URLなど)
@ngwords = (
'adultsexporn.biz',
'excisearch.net',
'goldenloto',
'frasesdecine',
'9.jp',
'703.jp',
'href=',
'sex',
'rgip.org');
#============#
# 設定完了 #
#============#
# bodyタグの定義
# $body = "
\n";
$body = "\n\n";
# 基本処理を定義
&decode;
&axs_check;
if ($mode eq "regist") { ®ist; }
elsif ($mode eq "form" && $in{'pview'} eq "on") { &preview; }
elsif ($mode eq "form" && $in{'pview'} ne "on") { ®ist; }
elsif ($mode eq "msgview") { &msgview; }
elsif ($mode eq "allread") { &allread; }
elsif ($mode eq "new_sort") { &new_sort; }
elsif ($mode eq "usr_del") { &usr_del; }
elsif ($mode eq "usr_edt") { &usr_edt; }
elsif ($mode eq "find") { &find; }
elsif ($mode eq "howto") { &howto; }
elsif ($mode eq "past") { &past_view; }
&list_view;
#----------------#
# アクセス制限 #
#----------------#
sub axs_check {
# ホスト名を取得
&get_host;
local($flag)=0;
foreach (@deny) {
next if (!$_);
s/\*/\.\*/g;
if ($host =~ /$_/i) { $flag=1; last; }
}
if ($flag) {
$mode = "";
# エラー表示
# &header;
# print "あなたと同一のホストの方が過去不適切な発言を行ったため
残念ながら現在アクセスできません\n";
# print "Your IP address and other information is logged.\n\n";
# exit;
}
}
sub chk_content {
local($chkword) = $_[0];
local($flag)=0;
foreach (@ngwords) {
next if (!$_);
s/\*/\.\*/g;
if ($chkword =~ /$_/i) { $flag=1; last; }
}
return ($flag);
}
#----------------#
# デコード処理 #
#----------------#
sub decode {
local($buffer, $name, $value, @pairs);
$post_flag=0;
if ($ENV{'REQUEST_METHOD'} eq "POST") {
$post_flag=1;
if ($ENV{'CONTENT_LENGTH'} > 51200) { &error("投稿量が大きすぎます"); }
read(STDIN, $buffer, $ENV{'CONTENT_LENGTH'});
} else { $buffer = $ENV{'QUERY_STRING'}; }
@pairs = split(/&/, $buffer);
foreach (@pairs) {
($name, $value) = split(/=/);
$value =~ tr/+/ /;
$value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg;
# S-JIS変換
&jcode'convert(*value, "sjis", "", "z");
# タグ処理
if ($tagkey) { $value =~ s/<>/<>/g; }
else {
$value =~ s/&/&/g;
$value =~ s/</g;
$value =~ s/>/>/g;
}
# 不要な改行を削除
if ($name ne "message") {
$value =~ s/\r//g;
$value =~ s/\n//g;
}
$in{$name} = $value;
}
$mode = $in{'mode'};
$page = $in{'page'};
if ($page eq "") { $page = 0; }
$in{'url'} =~ s/^http\:\/\///;
$in{'pastlog'} =~ s/\D//g;
# 強制改行
if (($mode eq "form" && $in{'pview'} ne "on" && $in{'wrap'} eq "hard") || ($mode eq "regist" && $in{'wrap'} eq "hard")) {
&get_agent;
$bytes = $com_wid;
if ($in{'action'} eq "res_msg") { $bytes += 2; }
local($tmp) = '';
while (length $in{'message'}) {
($folded, $in{'message'}) = &fold ($in{'message'},$bytes);
$tmp .= "$folded
";
}
$in{'message'} = $tmp;
}
# 改行コード処理
$in{'message'} =~ s/\r\n/
/g;
$in{'message'} =~ s/\r/
/g;
$in{'message'} =~ s/\n/
/g;
while ($in{'message'} =~ /
$/) { $in{'message'} =~ s/
$//g; }
# タイムゾーンを日本時間に合わせる
$ENV{'TZ'} = "JST-9";
}
#----------------#
# 書き込み処理 #
#----------------#
sub regist {
# POST限定
if ($MethPost && !$post_flag) { &error("不正なアクセスです"); }
# フォームチェック
if ($in{'name'} eq "" || $in{'message'} eq "")
{ &error("名前又はメッセージに記入モレがあります"); }
# E-Mail入力
if ($in_email && $in{'email'} !~ /[\w\.\-]+\@[\w\.\-]+\.[a-zA-Z]{2,5}/)
{ &error("E-Mailの入力が不正です"); }
$n = chk_content($in{'message'});
if ($n > 0) {
&after;
}
$n = $in{'message'} =~ tr/\xa1-\xfe/\xa1-\xfe/;
if ($n == 0) {
&after;
}
# URL入力
# if ($in{'no'} eq 'new' && !$in{'url'}) { &error("設置スクリプトのURL記述は必須です"); }
# 題名入力
if ($in{'sub'} eq "") { &error("題名の入力モレです"); }
# ロック処理
&lock if ($lockkey);
# ログファイル読み込み
open(IN,"$logfile") || &error("Open Error : $logfile");
@lines = ;
close(IN);
# カウントファイルをアップ
$count = shift(@lines);
$count =~ s/\n//;
if ($count == 9999) { $count = 1; } else { $count++; }
# 二重投稿の禁止
$flag=0;
foreach (@lines) {
($kno,$kre,$kx,$ktt,$kem,$kurl,$kname,$kdate,$kmsg) = split(/<>/);
if ($in{'name'} eq $kname && $in{'message'} eq $kmsg) { $flag=1; last; }
}
if ($flag) { &error("二重投稿は禁止です"); }
# クッキーを発行
&set_cookie;
# パスワード暗号化
if ($in{'pwd'} ne "") { $ango = &encrypt($in{'pwd'}); }
# 時間を取得
$times = time;
$date = &get_time("$times", "log");
## --- 親記事の場合
if ($in{'no'} eq 'new') {
unshift (@lines,"$count<>no<>0<>$in{'sub'}<>$in{'email'}<>$in{'url'}<>$in{'name'}<>$date<>$in{'message'}<>$times<>$host<>$ango<>$in{'wrap'}<>$count<>$in{'smail'}<>0<>\n");
@new = @lines;
}
## --- レス記事の場合
else {
## ツリーソート「あり」
if ($top_sort) {
@new=();
@tmp=();
$flag=0;
foreach (@lines) {
chop;
($no,$reno,$lx,$t,$e,$u,$n,$d,$m,$tm,$h,$a,$w,$OYA,$smail,$res) = split(/<>/);
if ($bot_res && $flag == 1 && $lx2 > $lx && $OYA == $in{'oya'}) {
$flag=2;
push(@new,"$count<>$in{'no'}<>$lx2<>$in{'sub'}<>$in{'email'}<>$in{'url'}<>$in{'name'}<>$date<>$in{'message'}<>$times<>$host<>$ango<>$in{'wrap'}<>$in{'oya'}<>$in{'smail'}<>0<>\n");
}
# 同一ツリーの記事を @new に配列分割(直レス)
if ($no == $in{'no'}) {
$res++;
push(@new,"$no<>$reno<>$lx<>$t<>$e<>$u<>$n<>$d<>$m<>$tm<>$h<>$a<>$w<>$OYA<>$smail<>$res<>\n");
}
# 同一ツリーの記事を @new に配列分割
elsif ($in{'oya'} == $OYA) { push(@new,"$_\n"); }
# 別ツリーの記事を @tmp に配列分割
else { push(@tmp,"$_\n"); }
if ($no == $in{'no'}) {
$flag=1;
$lx2 = $lx + 1;
if (!$bot_res) {
push(@new,"$count<>$in{'no'}<>$lx2<>$in{'sub'}<>$in{'email'}<>$in{'url'}<>$in{'name'}<>$date<>$in{'message'}<>$times<>$host<>$ango<>$in{'wrap'}<>$in{'oya'}<>$in{'smail'}<>0<>\n");
}
}
}
if ($bot_res && $flag != 2) {
push(@new,"$count<>$in{'no'}<>$lx2<>$in{'sub'}<>$in{'email'}<>$in{'url'}<>$in{'name'}<>$date<>$in{'message'}<>$times<>$host<>$ango<>$in{'wrap'}<>$in{'oya'}<>$in{'smail'}<>0<>\n");
}
# 配列最終結合
push(@new,@tmp);
}
## ツリーソート「なし」
else {
@new=();
$flag=0;
foreach (@lines) {
chop;
($no,$reno,$lx,$t,$e,$u,$n,$d,$m,$tm,$h,$a,$w,$OYA,$smail,$res) = split(/<>/);
if ($bot_res && $flag == 1 && $lx2 > $lx && ($reno ne $in{'no'} || $OYA != $in{'oya'})) {
$flag=2;
push(@new,"$count<>$in{'no'}<>$lx2<>$in{'sub'}<>$in{'email'}<>$in{'url'}<>$in{'name'}<>$date<>$in{'message'}<>$times<>$host<>$ango<>$in{'wrap'}<>$in{'oya'}<>$in{'smail'}<>0<>\n");
}
# 直親記事
if ($no == $in{'no'}) {
$res++;
push(@new,"$no<>$reno<>$lx<>$t<>$e<>$u<>$n<>$d<>$m<>$tm<>$h<>$a<>$w<>$OYA<>$smail<>$res<>\n");
} else { push(@new,"$_\n"); }
if ($no == $in{'no'}) {
$flag=1;
$lx2 = $lx + 1;
if (!$bot_res) {
push (@new,"$count<>$no<>$lx2<>$in{'sub'}<>$in{'email'}<>$in{'url'}<>$in{'name'}<>$date<>$in{'message'}<>$times<>$host<>$ango<>$in{'wrap'}<>$in{'oya'}<>$in{'smail'}<>0<>\n");
}
}
}
if ($bot_res && $flag != 2) {
push(@new,"$count<>$in{'no'}<>$lx2<>$in{'sub'}<>$in{'email'}<>$in{'url'}<>$in{'name'}<>$date<>$in{'message'}<>$times<>$host<>$ango<>$in{'wrap'}<>$in{'oya'}<>$in{'smail'}<>0<>\n");
}
}
}
# 最大記事数処理
@PAST=();
if (@new > $max) {
foreach (0 .. $#new) {
# 最終尾ファイルを配列から抜き出し過去ログ配列へ
local($p_file) = pop(@new);
push(@PAST,$p_file);
local($no,$reno,$lx) = split(/<>/, $p_file);
if ($#new+1 <= $max && $reno eq 'no') {
last;
}
}
}
# ログを更新
unshift(@new,"$count\n");
open(OUT,">$logfile") || &error("Write Error : $logfile");
print OUT @new;
close(OUT);
# 過去ログ処理
if ($pastkey) { &pastlog; }
# ファイルロック解除
&unlock if ($lockkey);
# メール通知
if ($mailing == 2) { &mail_to; }
elsif ($mailing == 1 && $in{'email'} ne "$mailto") { &mail_to; }
# 書きこみ後処理
if ($in{'pview'} ne "on") { &after; }
else { &list_view; }
}
#------------------------#
# 書きこみ後メッセージ #
#------------------------#
sub after {
if ($in{'url'}) { $in{'url'} = "http://$in{'url'}"; }
# ツリートップ移動処理の場合は書き込み後はトップページ
if ($top_sort) { $page = 0; }
# 引用色
if ($refcolor) {
$in{'message'} =~ s/([\>]|^)(>[^<]*)/$1$2<\/font>/g;
}
# 図表モード
if ($in{'wrap'} eq "pre") {
$in{'message'} =~ s/
/\n/g;
$in{'message'} = "$in{'message'}
";
}
if ($in{'smail'} eq "on") { $in{'email'} = '非表示'; }
&header;
print <<"EOM";
正常に書きこみが完了しました
おなまえ:$in{'name'}
Eメール:$in{'email'} $smail
URL :$in{'url'}
タイトル:$in{'sub'}
メッセージ
$in{'message'}
|