#!/usr/bin/perl

#┌─────────────────────────────────
#│ Web Forum for Admin v3.06 (2001/07/14)
#│ Copyright(C) Kent Web 2001
#│ webmaster@kent-web.com
#│ http://www.kent-web.com/
#└─────────────────────────────────
$ver = 'WF ADMIN v3.06';
#┌─────────────────────────────────
#│[ 注意事項 ]
#│ 1. このスクリプトはフリーソフトです。このスクリプトを使用した
#│    いかなる損害に対して作者は一切の責任を負いません。
#│ 2. 設置に関する質問はサポート掲示板にお願いいたします。
#│    直接メールによる質問は一切お受けいたしておりません。
#└─────────────────────────────────

#============#
#  設定項目  #
#============#

# 文字コードライブラリ
require './jcode.pl';

# パスワード (半角英数字で)
$pass = '0123';

# スクリプト名
$script  = './wf_admin.cgi';

# ログファイル
$logfile = './wf_log.cgi';

# 掲示板のファイル
$bbsurl  = './wforum.cgi';

# タグ処理 (0=no 1=yes)
$tagkey = 0;

# ロックファイル (wforum.cgiの設定に合わせること）
$lockfile = './lock/wforum.lock';

# ロックファイル機構
#   0 : 行なわない
#   1 : 行なう（symlink関数式）
#   2 : 行なう（mkdir関数式）
$lockkey = 0;

# methodの形式 (POST/GET)
$method = 'POST';

# 記事の更新は method=POST 限定 (0=no 1=yes)
$MethPost = 1;

# bodyタグ
$body = '<BODY BGCOLOR="#F1F1F1" TEXT="#000000" link="#0000E3" vlink="#008080">';

#============#
#  設定完了  #
#============#

&decode;
if ($mode eq "log_view") { &log_view; }
if ($mode eq "edit1") { &edit1; }
if ($mode eq "edit2") { &edit2; }
if ($mode eq "delete") { &delete; }
&enter;

#----------------#
#  ログ閲覧画面  #
#----------------#
sub log_view {
	if ($in{'pass'} ne "$pass") { &error("パスワードが違います"); }

	# ログを開く
	open(IN,"$logfile") || &error("Open Error : $logfile");
	@lines = <IN>;
	close(IN);
	shift(@lines);

	&header;
	print <<"EOM";
<small>
  <OL>
  <LI>記事を<B>削除</B>する場合、削除する記事の<B>チェックボックス</B>にチェックを付けて削除キーを押してください。
  <LI>記事を<B>編集</B>する場合には「題名」のリンク部をクリックしてください。
  </OL>
</small>
<form action="$script" method=$method>
<input type=hidden name=mode value="delete">
<input type=hidden name=pass value="$in{'pass'}">
<input type=submit value="削除する"><input type=reset value="リセット">
EOM

	$x = 0;
	print "<DL>\n";
	foreach (@lines) {
		($no,$reno,$lx,$sub,$email,$url,$name,
			$date,$msg,$time,$host,$pw,$wrap,$oya) = split(/<>/);

		$msg =~ s/<br>/ /g;
		$msg =~ s/</&lt;/g;
		$msg =~ s/>/&gt;/g;
		if (length($msg) > 60) { $msg = substr($msg,0,58); $msg .= '..'; }

		while ($x > $lx) { print "</DL>\n"; $x--; }
		while ($x < $lx) { print "<DL>\n"; $x++; }

		if ($no eq "$oya") {
			$sub = "<B>$sub</B>";
			print "<hr><DT><input type=checkbox name=treedel value=\"$no\">\n";
			print "<font color=#dd0000 size=2>[ツリーの一括削除]</font>\n";
		}

		print "<DT><input type=checkbox name=del value=\"$no\"> ";
		print "<a href=$script?mode=edit1&no=$no&pass=$in{'pass'}>$sub</a>\n";
		print "<small> - <B>$name</B> $date ($host) <font color=#800000>No\.$no</font></small>\n";
		print "<DD><font size=1>$msg</font>\n";

	}
	while ($x > 0) { print "</DL>\n"; $x--; }
	print "</DL>\n</form>\n<hr>\n";

	# 著作権表示（削除禁止）
	print "<center><small><!-- $ver -->\n";
	print "- <a href='http://www.kent-web.com/' target='_top'>WebForum</a> -\n";
	print "</small></center>\n</body>\n</html>\n";
	exit;
}

#----------------#
#  記事編集画面  #
#----------------#
sub edit1 {
	if ($in{'pass'} ne "$pass") { &error("パスワードが違います"); }

	# ログを開く
	open(IN,"$logfile") || &error("Can't open $logfile");
	@lines = <IN>;
	close(IN);
	shift(@lines);

	$flag=0;
	foreach (@lines) {
		($no,$reno,$x,$sub,$email,$url,$name,$date,
			$msg,$time,$host,$pw,$wrap,$oya,$smail,$res) = split(/<>/);

		if ($in{'no'} == $no) { $flag=1; last; }
	}
	if (!$flag) { &error("編集対象記事が見当たりません"); }

	$msg =~ s/<br>/\r/g;
	&get_agent;
	if ($smail eq "on") { $check = 'checked'; }

	# 編集フォームを出力
	&header;
	print <<"EOM";
<center>
■変更したい部分のみ修正し、送信キーを押してください
<P>
<form action="$script" method=$method>
<input type=hidden name=mode value="edit2">
<input type=hidden name=action value="edit">
<input type=hidden name=no value="$in{'no'}">
<input type=hidden name=pass value="$in{'pass'}">
<table border=0>
<tr>
  <td><B>投稿者</B></td>
  <td><input type=text name=name value="$name" size=$nam_wid></td>
</tr>
<tr>
  <td><B>Ｅメール</B></td>
  <td>
	<input type=text name=email value="$email" size=$nam_wid>
	<input type=checkbox name=smail value="on" $check>非表\示
  </td>
</tr>
<tr>
  <td><B>タイトル</B></td>
  <td><input type=text name=sub value="$sub" size=$subj_wid>
  <input type=submit value="送信する"><input type=reset value="リセット"></td>
</tr>
<tr>
  <td colspan=2><B>コメント</B><br>
  <textarea name=msg cols=$com_wid rows=18 wrap=soft>$msg</textarea></td>
</tr>
<tr>
  <td><B>ＵＲＬ</B></td>
  <td><input type=text name=url value="http://$url" size=$url_wid></td>
</tr>
</table>
</form></center>
</body>
</html>
EOM
	exit;
}

#----------------#
#  記事編集処理  #
#----------------#
sub edit2 {
	# POST限定
	if ($MethPost && !$post_flag) { &error("不正なアクセスです"); }

	if ($in{'pass'} ne "$pass") { &error("パスワードが違います"); }

	# ロック開始
	&lock if ($lockkey);

	# ログを開く
	open(IN,"$logfile") || &error("Open Error : $logfile","LK");
	@lines = <IN>;
	close(IN);
	$top = shift(@lines);

	$in{'url'} =~ s/^http\:\/\///;

	$flag=0;
	@new=();
	foreach (@lines) {
		chop;
		($no,$reno,$x,$sub,$email,$url,$name,$date,
			$msg,$time,$host,$pw,$wrap,$oya,$smail,$res) = split(/<>/);

		if ($in{'no'} == $no) {
			$flag=1;
			$_ = "$no<>$reno<>$x<>$in{'sub'}<>$in{'email'}<>$in{'url'}<>$in{'name'}<>$date<>$in{'msg'}<>$time<>$host<>$pw<>$wrap<>$oya<>$in{'smail'}<>$res<>";
		}
		push(@new,"$_\n");
	}
	if (!$flag) { &error("編集対象記事が見当たりません","LK"); }

	# ログを更新
	unshift(@new,$top);
	open(OUT,">$logfile") || &error("Write Error : $logfile","LK");
	print OUT @new;
	close(OUT);

	# ロック解除
	&unlock if ($lockkey);

	# 初期画面に戻る
	&log_view;
}

#----------------#
#  記事削除処理  #
#----------------#
sub delete {
	# POST限定
	if ($MethPost && !$post_flag) { &error("不正なアクセスです"); }

	if ($DEL[0] eq "" && $TDEL[0] eq "") { &error("削除情報がありません"); }
	if ($in{'pass'} ne "$pass") { &error("パスワードが違います"); }

	# ロック開始
	&lock if ($lockkey);

	# ログを開く
	open(IN,"$logfile") || &error("Open Error : $logfile","LK");
	@lines = <IN>;
	close(IN);
	$head = shift(@lines);

	$key1=0;
	$key2=0;
	@temp=();
	if ($TDEL[0]) {
		$key1=1; # ツリー削除あり
		foreach (@lines) {
			$tflag=0;
			($no,$reno,$x,$sub,$email,$url,$name,
				$date,$msg,$time,$host,$pw,$wrap,$oya) = split(/<>/);

			foreach $tdel (@TDEL) {
				if ($oya eq "$tdel") { $tflag=1; last; }
			}
			if ($tflag == 0) { push(@temp,$_); }
		}
		@lines = @temp;
	}

	# 削除情報とログをマッチし、@new に格納
	if ($DEL[0]) {
		$key2=1; # 個別削除あり
		@new=();
		foreach (@lines) {
			chop;
			$flag  = 0;
			$oflag = 0;
			($no,$reno,$x,$sub,$email,$url,$name,$date,
				$msg,$time,$host,$pw,$wrap,$oya,$smail,$res) = split(/<>/);
			if ($no == $oya) { $oflag=1; }
			foreach $del (@DEL) {
				if ($oflag == 0 && $no == $del) { $flag=1; last; }
				if ($oflag == 1 && $no == $del) {
					$_ = "$no<>$reno<>$lx<>(削除)<><><>(削除)<>$date<>(削除されました)<>$time<>$host<><><>$oya<>$smail<>$res<>";
					last;
				}
			}
			if ($flag == 0) { push(@new,"$_\n"); }
		}
	}

	# 配列を定義
	if ($key1 == 1 && $key2 == 0) { @data = @lines; }
	else { @data = @new; }

	# ログを更新
	unshift(@data,$head);
	open(OUT,">$logfile") || &error("Write Error : $logfile","LK");
	print OUT @data;
	close(OUT);

	# ロック解除
	&unlock if ($lockkey);

	# 初期画面に戻る
	&log_view;
}

#------------#
#  入室画面  #
#------------#
sub enter {
	&header;
	print "<center><h4>パスワードを入力してください</h4>\n";
	print "<form action=\"$script\" method=$method>\n";
	print "<input type=hidden name=mode value=\"log_view\">\n";
	print "<input type=password name=pass size=8>";
	print "<input type=submit value=\" 認証 \">\n";
	print "</form></center>\n<!-- $ver -->\n</body>\n</html>\n";
	exit;
}

#--------------#
#  HTMLヘッダ  #
#--------------#
sub header {
	print "Content-type: text/html\n\n";
	print <<"EOM";
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html lang="ja">
<head>
<STYLE type="text/css">
<!--
body,tr,td,th { font-size:10pt; }
small         { font-size:9pt; }
-->
</STYLE>
<META HTTP-EQUIV="Content-type" CONTENT="text/html; charset=Shift_JIS">
<title>Web Forum for Admin</title></head>
$body
[<a href="$bbsurl" target="_top">戻る</a>]
<table width="100%"><tr><th bgcolor="#8080C0">
<font color="#FFFFFF">管理モード</font>
</th></tr></table>
<P>
EOM
}

#--------------------#
#  フォームデコード  #
#--------------------#
sub decode {
	local(@pairs,$buffer,$name,$value);

	$post_flag=0;
	if ($ENV{'REQUEST_METHOD'} eq "POST") {
		$post_flag=1;
		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/<>/&lt;&gt;/g; }
		else {
			$value =~ s/&/&amp;/g;
			$value =~ s/</&lt;/g;
			$value =~ s/>/&gt;/g;
		}

		# 改行処理
		if ($name eq "msg") {
			$value =~ s/\r\n/<br>/g;
			$value =~ s/\r/<br>/g;
			$value =~ s/\n/<br>/g;
		} else {
			$value =~ s/\r//g;
			$value =~ s/\n//g;
		}

		# 削除データをプール
		if ($name eq "del") { push(@DEL,$value); }
		elsif ($name eq "treedel") { push(@TDEL,$value); }

		$in{$name} = $value;
	}
	$mode = $in{'mode'};
}

#--------------#
#  エラー処理  #
#--------------#
sub error {
	&unlock if ($lockkey && $_[1] eq "LK");

	&header;
	print "<center><h3>ERROR !</h3>\n";
	print "<P><font color='#CC0000'>$_[0]</font>\n";
	print "<P></center>\n</body>\n</html>\n";
	exit;
}

#--------------------#
#  フォーム長を調整  #
#--------------------#
sub get_agent {
	$agent = $ENV{'HTTP_USER_AGENT'};

	if ($agent =~ /MSIE 3/i) {
		$nam_wid  = 30;
		$subj_wid = 40;
		$com_wid  = 64;
		$url_wid  = 46;
	} elsif ($agent =~ /MSIE 4/i || $agent =~ /MSIE 5/i) {
		$nam_wid  = 30;
		$subj_wid = 40;
		$com_wid  = 64;
		$url_wid  = 78;
	} else {
		$nam_wid  = 20;
		$subj_wid = 27;
		$com_wid  = 62;
		$url_wid  = 52;
	}
}

#--------------#
#  ロック処理  #
#--------------#
sub lock {
	local($retry) = 5;
	# 3分以上古いロックは削除する
	if (-e $lockfile) {
		local($mtime) = (stat($lockfile))[9];
		if ($mtime < time - 180) { &unlock; }
	}
	# symlink関数式ロック
	if ($lockkey == 1) {
		while (!symlink(".", $lockfile)) {
			if (--$retry <= 0) { &error('Lock is busy'); }
			sleep(1);
		}
	# mkdir関数式ロック
	} elsif ($lockkey == 2) {
		while (!mkdir($lockfile, 0755)) {
			if (--$retry <= 0) { &error('Lock is busy'); }
			sleep(1);
		}
	}
}

#--------------#
#  ロック解除  #
#--------------#
sub unlock {
	if ($lockkey == 1) { unlink($lockfile); }
	elsif ($lockkey == 2) { rmdir($lockfile); }
}

#----------------#
#  ホスト名取得  #
#----------------#
sub get_host {
	$host = $ENV{'REMOTE_HOST'};
	$addr = $ENV{'REMOTE_ADDR'};

	if ($host eq "" || $host eq "$addr") {
		$host = gethostbyaddr(pack("C4", split(/\./, $addr)), 2);
	}
	if ($host eq "") { $host = $addr; }
}
