#!/usr/bin/perl

#┌─────────────────────────────────
#│ Aska BBS v3.5 (2006/08/15)
#│ Copyright (c) KentWeb
#│ webmaster@kent-web.com
#│ http://www.kent-web.com/
#└─────────────────────────────────
$ver = 'ASKA BBS v3.5';
#┌─────────────────────────────────
#│ [注意事項]
#│ 1. このスクリプトはフリーソフトです。このスクリプトを使用した
#│    いかなる損害に対して作者は一切の責任を負いません。
#│ 2. 設置に関する質問はサポート掲示板にお願いいたします。
#│    直接メールによる質問は一切お受けいたしておりません。
#└─────────────────────────────────
#
# 【ファイル構成例】
#
#  public_html (ホームディレクトリ)
#      |
#      +-- aska / aska.cgi    [705]
#                 askalog.cgi [606]
#                 jcode.pl    [604]

#-------------------------------------------------
#  ■基本設定
#-------------------------------------------------

# ライブラリ取り込み
require './jcode.pl';

# タイトル名
$title = "掲示板";

# タイトル文字の色
$tCol = "#666666";

# タイトル文字サイズ
$tSize = '24px';

# 本文文字サイズ
$bSize = '13px';

# 本文文字スタイル
$bFace = '"MS UI Gothic", "ＭＳ Ｐゴシック", Osaka';

# 記事題名の色
$subCol = "#666666";

# スクリプトURL
$script = './aska.cgi';

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

# 管理用パスワード
$pass = 'onsen';

# 最大記事数（これを超える記事は古い順に削除）
$max = 50;

# 戻り先URL
$home = "../index.html";

# タイトル画像を使う場合 (http://から画像を指定)
$ImgT = "";
$ImgW = 300;	# 横幅サイズ
$ImgH = 70;	# 縦幅サイズ

# bodyタグ
$body = '<body bgcolor="#ffffff" text="#000000" link="#999999" vlink="#333333" alink="#cccccc">';

# URLの自動リンク (0=no 1=yes)
$autolink = 1;

# 引用部色変更
#  1 : 色指定を行うと「引用部」を色変更します
#  2 : この機能を使用しない場合は何も記述しないで下さい ($refCol="";)
$refCol = "#777777";

# １ページあたりの記事表示件数
$pageLog = 10;

# メール通知機能
#  0 : 通知しない
#  1 : 通知する → 自分の投稿記事も通知する
#  2 : 通知する → 自分の投稿記事は通知しない
$mailing = 0;

# sendmailのパス（メール通知する場合）
$sendmail = '/usr/lib/sendmail';

# メール通知先アドレス（メール通知する場合）
$mailto = 'xxx@xxx.xxx';

# タグ広告挿入オプション
#  → <!-- 上部 --> <!-- 下部 --> の代わりに「広告タグ」を挿入。
#  → 広告タグ以外に、MIDIタグ や LimeCounter等のタグにも使用可能。
$banner1 = '<!-- 上部 -->';  # 表示部上部に挿入
$banner2 = '<!-- 下部 -->';  # 表示部下部に挿入

# 記事の更新は method=post に限定する場合（セキュリティ対策）
#  → 0=no 1=yes
$postonly = 1;

# 他サイトから投稿排除時に指定する場合（セキュリティ対策）
#  → 掲示板のURLをhttp://から書く
$baseUrl = '';

# 投稿制限（セキュリティ対策）
#  0 : しない
#  1 : 同一IPアドレスからの投稿間隔を制限する
#  2 : 全ての投稿間隔を制限する
$regCtl = 1;

# 制限投稿間隔（秒数）
#  → $regCtl での投稿間隔
$wait = 60;

# 禁止ワード
# → 投稿時禁止するワードをコンマで区切る
$no_wd = '';

# 日本語チェック（投稿時日本語が含まれていなければ拒否する）
# 0=No  1=Yes
$jp_wd = 0;

# URL個数チェック
# → 投稿コメント中に含まれるURL個数の最大値
$urlnum = 3;

# 投稿後の処理
#  → 掲示板自身のURLを記述しておくと、投稿後リロードします
#  → ブラウザを再読み込みしても二重投稿されない措置。
#  → Locationヘッダの使用可能なサーバのみ
$location = '';

# ホスト取得方法
# 0 : gethostbyaddr関数を使わない
# 1 : gethostbyaddr関数を使う
$gethostbyaddr = 0;

# アクセス制限（半角スペースで区切る、アスタリスク可）
#  → 拒否ホスト名を記述（後方一致）【例】*.anonymizer.com
$deny_host = '';
#  → 拒否IPアドレスを記述（前方一致）【例】210.12.345.*
$deny_addr = '';

# １回当りの最大投稿サイズ (bytes)
$maxData = 51200;

#-------------------------------------------------
#  ■設定完了
#-------------------------------------------------

&decode;
&axscheck;
if ($mode eq 'regist') { &regist; }
elsif ($mode eq 'find') { &find; }
elsif ($mode eq 'howto') { &howto; }
elsif ($mode eq 'admin') { &admin; }
elsif ($mode eq 'usrdel') { &usrdel; }
elsif ($mode eq 'check') { &check; }
&html;

#-------------------------------------------------
#  アクセス制限
#-------------------------------------------------
sub axscheck {
	# IP&ホスト取得
	$host = $ENV{'REMOTE_HOST'};
	$addr = $ENV{'REMOTE_ADDR'};
	if ($gethostbyaddr && ($host eq "" || $host eq $addr)) {
		$host = gethostbyaddr(pack("C4", split(/\./, $addr)), 2);
	}

	# IPチェック
	local($flg);
	foreach ( split(/\s+/, $deny_addr) ) {
		s/\./\\\./g;
		s/\*/\.\*/g;

		if ($addr =~ /^$_/i) { $flg = 1; last; }
	}
	if ($flg) {
		&error("アクセスを許可されていません");

	# ホストチェック
	} elsif ($host) {

		foreach ( split(/\s+/, $deny_host) ) {
			s/\./\\\./g;
			s/\*/\.\*/g;

			if ($host =~ /$_$/i) { $flg = 1; last; }
		}
		if ($flg) {
			&error("アクセスを許可されていません");
		}
	}
	if ($host eq "") { $host = $addr; }
}

#-------------------------------------------------
#  記事表示
#-------------------------------------------------
sub html {
	local($r_sub,$r_com);

	# クッキー取得
	local($cnam,$ceml,$curl,$cpwd) = &get_cookie;
	if (!$curl) { $curl = 'http://'; }

	# レス処理
	$in{'res'} =~ s/\D//g;
	if ($in{'res'}) {
		local($flag,$no,$dat,$nam,$eml,$sub,$com);

		open(IN,"$logfile");
		while (<IN>) {
			($no,$dat,$nam,$eml,$sub,$com) = split(/<>/);

			if ($in{'res'} == $no) { $flag = 1; last; }
		}
		close(IN);

		if (!$flag) { &error("該当記事が見つかりません"); }

		$sub =~ s/^Re://g;
		$sub =~ s/\[\d+\]\s?//g;
		$r_sub = "Re:[$no] $sub";
		$r_com = "&gt; $com";
		$r_com =~ s/<br>/\r&gt; /ig;
	}

	&header;
	print "<div align=\"center\">\n";
	print "$banner1<p>\n" if ($banner1 ne "<!-- 上部 -->");

	# タイトル
	if ($ImgT) {
		print "<img src=\"$ImgT\" width=\"$ImgW\" height=\"$ImgH\" alt=\"$title\">\n";
	} else {
		print "<b style=\"font-size:$tSize; color:$tCol;\">$title</b>\n";
	}

	print <<EOM;
<hr width="90%">
[<a href="$home" target="_top">トップに戻る</a>]
[<a href="$script?mode=howto">留意事項</a>]
[<a href="$script?mode=find">ワード検索</a>]
[<a href="$script?mode=admin">管理用</a>]
<hr width="90%"></div>
<blockquote>
<form action="$script" method="post">
<input type="hidden" name="mode" value="regist">
<table cellpadding="1" cellspacing="1">
<tr>
  <td nowrap><b>おなまえ</b></td>
  <td><input type="text" name="name" size="28" value="$cnam"></td>
</tr>
<tr>
  <td nowrap><b>Ｅメール</b></td>
  <td><input type="text" name="email" size="28" value="$ceml"></td>
</tr>
<tr>
  <td nowrap><b>タイトル</b></td>
  <td>
    <input type="text" name="sub" size="36" value="$r_sub">
    <input type="submit" value="送信する"><input type="reset" value="リセット">
  </td>
</tr>
<tr>
  <td colspan="2" nowrap>
    <b>メッセージ</b><br>
    <textarea name="comment" cols="56" rows="7">$r_com</textarea>
  </td>
</tr>
<tr>
  <td nowrap><b>参照先</b></td>
  <td><input type="text" name="url" size="50" value="$curl"></td>
</tr>
<tr>
  <td nowrap><b>削除キー</b></td>
  <td>
    <input type="password" name="pwd" size="8" maxlength="8" value="$cpwd">
	<small>(英数字で8文字以内)</small>
  </td>
</tr>
<tr>
  <td></td>
  <td><input type="checkbox" name="cook" value="on" checked>
	<small>クッキー情報保存</small>
  </td>
</tr>
</table>
</form>
</blockquote>
<dl>
EOM

	local($i) = 0;
	open(IN,"$logfile") || &error("Open Error: $logfile");
	while (<IN>) {
		$i++;
		next if ($i < $page + 1);
		next if ($i > $page + $pageLog);

		local($no,$dat,$nam,$eml,$sub,$com,$url) = split(/<>/);

		if ($eml) { $nam = "<a href=\"mailto:$eml\">$nam</a>"; }
		if ($autolink) { &auto_link($com); }
		if ($refCol) { $com =~ s/([\>]|^)(&gt;[^<]*)/$1<font color=\"$refCol\">$2<\/font>/g; }

		print "<dt><hr>[<b>$no</b>] <font color=\"$subCol\"><b>$sub</b></font>\n";
		print "投稿者：<b>$nam</b> 投稿日：$dat\n";
		print "[<a href=\"$script?res=$no\">返信</a>]<br><br><dd>$com";
		print "<p><a href=\"$url\" target=\"_blank\">$url</a>" if ($url);
		print "<br><br>\n";
	}
	close(IN);

	print <<EOM;
<dt><hr>
</dl>
<table width="100%">
<tr><td nowrap>
<form action="$script" method="post">
EOM

	# ページ繰り越し
	local($next) = $page + $pageLog;
	local($back) = $page - $pageLog;

	if ($back >= 0) {
		print "<input type=\"submit\" name=\"page$back\" value=\"前の$pageLog件\">\n";
	}
	if ($next < $i) {
		print "<input type=\"submit\" name=\"page$next\" value=\"次の$pageLog件\">\n";
	}

	# 著作権表示（削除禁止）
	print <<EOM;
</form></td>
<td nowrap align="right">
<form action="$script" method="post">
<input type="hidden" name="mode" value="usrdel">
記事No<input type="text" name="no" size="3">
削除キー<input type="password" name="pwd" size="4" maxlength="8">
<input type="submit" value="削除"></td></form>
</tr></table>
<div align="center">
$banner2
<p><span style="font-size:10px;font-family:Verdana,Helvetica,Arial">
- <a href="http://www.kent-web.com/" target="_top">ASKA BBS</a> -
<!-- $ver --></span></div>
</body>
</html>
EOM
	exit;
}

#-------------------------------------------------
#  記事書込
#-------------------------------------------------
sub regist {
	# 投稿チェック
	if ($postonly && !$post_flag) { &error("不正なアクセスです"); }
	if ($baseUrl) { &refCheck; }

	# チェック
	if ($no_wd) { &no_wd; }
	if ($jp_wd) { &jp_wd; }
	if ($urlnum > 0) { &urlnum; }

	# フォーム内容をチェック
	local($err);
	if ($in{'name'} eq "") { $err .= "名前が入力されていません<br>"; }
	if ($in{'comment'} eq "") { $err .= "コメントが入力されていません<br>"; }
	if ($in{'email'} && $in{'email'}!~ /^[\w\.\-]+\@[\w\.\-]+\.[a-zA-Z]{2,6}$/) {
		$err .= "Ｅメールの入力内容が不正です<br>";
	}
	if ($err) { &error($err); }

	if ($in{'url'} eq "http://") { $in{'url'} = ""; }
	if ($in{'sub'} eq "") { $in{'sub'} = "無題"; }

	# 先頭記事読み取り
	local($top,$i,$flag,@data);
	open(DAT,"+< $logfile") || &error("Open Error: $logfile");
	eval 'flock(DAT, 2);';
	$top = <DAT>;

	# 重複投稿チェック
	local($no,$dat,$nam,$eml,$sub,$com,$url,$hos,$pw,$tim) = split(/<>/, $top);
	if ($in{'name'} eq $nam && $in{'comment'} eq $com) {
		close(DAT);
		&error("二重投稿は禁止です");
	}

	# 連続投稿チェック
	local($time) = time;
	if ($regCtl == 1) {
		if ($host eq $hos && $time - $tim < $wait) { $flag = 1; }
	} elsif ($regCtl == 2) {
		if ($time - $tim < $wait) { $flag = 1; }
	}
	if ($flag) {
		close(DAT);
		&error("現在投稿制限中です。もうしばらくたってから投稿をお願いします");
	}

	# 記事No採番
	$no++;

	# 削除キー暗号化
	local($pwd);
	if ($in{'pwd'} ne "") { $pwd = &encrypt($in{'pwd'}); }

	# 時間取得
	local($min,$hour,$mday,$mon,$year,$wday) = (localtime($time))[1..6];
	local(@wk) = ('Sun','Mon','Tue','Wed','Thu','Fri','Sat');
	local($date) = sprintf("%04d/%02d/%02d(%s) %02d:%02d",
			$year+1900,$mon+1,$mday,$wk[$wday],$hour,$min);

	# 記事数調整
	$data[0] = $top;
	while (<DAT>) {
		$i++;
		push(@data,$_);

		last if ($i >= $max-1);
	}

	# 更新
	seek(DAT, 0, 0);
	print DAT "$no<>$date<>$in{'name'}<>$in{'email'}<>$in{'sub'}<>$in{'comment'}<>$in{'url'}<>$host<>$pwd<>$time<>\n";
	print DAT @data;
	truncate(DAT, tell(DAT));
	close(DAT);

	# クッキーを記憶
	if ($in{'cook'} eq 'on') {
		&set_cookie($in{'name'},$in{'email'},$in{'url'},$in{'pwd'});
	}

	# メール通知処理
	if ($mailing == 1 || ($mailing == 2 && $in{'email'} ne $mailto)) { &mail_to; }

	# リロード
	if ($location) {
		if ($ENV{'PERLXS'} eq "PerlIS") {
			print "HTTP/1.0 302 Temporary Redirection\r\n";
			print "Content-type: text/html\n";
		}
		print "Location: $location?\n\n";
		exit;
	} else {
		&message("投稿は正常に受理されました");
	}
}

#-------------------------------------------------
#  ワード検索
#-------------------------------------------------
sub find {
	&header;
	print <<EOM;
<form action="$script">
<input type="submit" value="&lt; 掲示板">
</form>
<ul>
<li>キーワードを入力し、検索ボタンを押してください。
<li>キーワードはスペースで区切って複数指定することができます。
<form action="$script" method="post">
<input type="hidden" name="mode" value="find">
キーワード <input type="text" name="word" size="38" value="$in{'word'}">
条件 <select name="cond">
EOM

	foreach ("AND", "OR") {
		if ($in{'cond'} eq $_) {
			print "<option value=\"$_\" selected>$_\n";
		} else {
			print "<option value=\"$_\">$_\n";
		}
	}
	print "</select> 表\示 <select name=\"view\">\n";
	foreach (10,15,20,25,30) {
		if ($in{'view'} == $_) {
			print "<option value=\"$_\" selected>$_件\n";
		} else {
			print "<option value=\"$_\">$_件\n";
		}
	}

	print <<EOM;
</select>
<input type="submit" value=" 検索 ">
</ul>
EOM

	# ワード検索の実行と結果表示
	if ($in{'word'} ne "") {

		# 入力内容を整理
		$in{'word'} =~ s/\x81\x40/ /g;
		local(@wd) = split(/\s+/, $in{'word'});

		# 検索
		print "<dl>\n";
		local($i) = 0;
		local(@find);
		open(IN,"$logfile") || &error("Open Error: $logfile");
		while (<IN>) {
			local($no,$dat,$nam,$eml,$sub,$com,$url) = split(/<>/);

			$flag = 0;
			foreach $wd (@wd) {
				if (index("$no $nam $eml $sub $com $url",$wd) >= 0) {
					$flag = 1;
					if ($in{'cond'} eq 'OR') { last; }
				} else {
					if ($in{'cond'} eq 'AND') {
						$flag = 0;
						last;
					}
				}
			}
			if ($flag) {
				$i++;
				next if ($i < $page + 1);
				next if ($i > $page + $in{'view'});

				push(@find,$_);
			}
		}
		close(IN);

		print "<dt>▽ <b>$in{'word'}</b> に関連する記事は<b>$i</b>件見つかりました。\n";

		foreach (@find) {
			local($no,$ymd,$nam,$eml,$sub,$com,$url) = split(/<>/);

			if ($eml) { $nam="<a href=\"mailto:$eml\">$nam</a>"; }
			if ($url) { $com .= "<p><a href=\"$url\" target=\"_blank\">$url</a>"; }

			print "<dt><hr>[<b>$no</b>] <b style=\"color:$subCol\">$sub</b> ";
			print "投稿者：<b>$nam</b> 投稿日：$ymd<br><br>\n";
			print "<dd>$com<br><br>\n";
		}

		print "<dt><hr></dl>\n";

		$next = $page + $in{'view'};
		$back = $page - $in{'view'};

		if ($back >= 0) {
			print "<input type=\"submit\" name=\"page$back\" value=\"前の$in{'view'}件\">\n";
		}
		if ($next < $i) {
			print "<input type=\"submit\" name=\"page$next\" value=\"次の$in{'view'}件\">\n";
		}
	}

	print <<EOM;
</form>
</body>
</html>
EOM
	exit;
}

#-------------------------------------------------
#  管理モード
#-------------------------------------------------
sub admin {
	# 認証
	if ($in{'pass'} eq "") { &enter; }
	elsif ($in{'pass'} ne $pass) { &error("パスワードが違います"); }

	# 削除処理
	if ($in{'job'} eq "dele" && $in{'no'}) {

		# 削除情報
		local(@del) = split(/\0/, $in{'no'});

		# 削除情報をマッチング
		local(@data);
		open(DAT,"+< $logfile") || &error("Open Error: $logfile");
		eval 'flock(DAT, 2);';
		while (<DAT>) {
			local($no) = split(/<>/);

			local($flag);
			foreach $del (@del) {
				if ($no == $del) { $flag = 1; last; }
			}
			if (!$flag) { push(@data,$_); }
		}

		# 更新
		seek(DAT, 0, 0);
		print DAT @data;
		truncate(DAT, tell(DAT));
		close(DAT);

	# 修正画面
	} elsif ($in{'job'} eq "edit" && $in{'no'}) {

		if ($in{'no'} =~ /\0/) {
			&error("修正の場合選択する記事は１つのみです");
		}

		# 記事抽出
		local($no,$dat,$nam,$eml,$sub,$com,$url);
		open(IN,"$logfile") || &error("Open Error: $logfile");
		while (<IN>) {
			($no,$dat,$nam,$eml,$sub,$com,$url) = split(/<>/);

			if ($in{'no'} == $no) { last; }
		}
		close(IN);

		# 修正フォームへ
		&edit_form($no,$dat,$nam,$eml,$sub,$com,$url);

	# 修正実行
	} elsif ($in{'job'} eq "edit2") {

		if ($in{'url'} eq "http://") { $in{'url'} = ""; }
		if ($in{'sub'} eq "") { $in{'sub'} = "無題"; }

		# 読み出し
		local(@data);
		open(DAT,"+< $logfile") || &error("Open Error: $logfile");
		eval 'flock(DAT, 2);';
		while (<DAT>) {
			local($no,$dat,$nam,$eml,$sub,$com,$url,$hos,$pwd,$tim) = split(/<>/);

			if ($in{'no'} == $no) {
				$_ = "$no<>$dat<>$in{'name'}<>$in{'email'}<>$in{'sub'}<>$in{'comment'}<>$in{'url'}<>$hos<>$pwd<>$tim<>\n";
			}
			push(@data,$_);
		}

		# 更新
		seek(DAT, 0, 0);
		print DAT @data;
		truncate(DAT, tell(DAT));
		close(DAT);

		# 完了メッセージ
		&message("記事を修正しました");
	}

	# 削除画面を表示
	&header;
	print <<EOM;
<form action="$script">
<input type="submit" value="&lt; 掲示板">
</form>
<ul>
<li>処理を選択して送信ボタンを押してください。
</ul>
<form action="$script" method="post">
<input type="hidden" name="mode" value="admin">
<input type="hidden" name="pass" value="$in{'pass'}">
処理：
<select name="job">
<option value="edit">修正
<option value="dele">削除
</select>
<input type="submit" value="送信する">
<dl>
EOM

	# 記事を展開
	open(IN,"$logfile") || &error("Open Error: $logfile");
	while (<IN>) {
		local($no,$dat,$nam,$eml,$sub,$com,$url,$hos) = split(/<>/);

		if ($eml) { $nam="<a href=\"mailto:$eml\">$nam</a>"; }
		$com =~ s/<[^>]*(>|$)//g;
		if (length($com) > 60) {
			$com = substr($com,0,60) . '...';
		}

		print "<dt><hr><input type=\"checkbox\" name=\"no\" value=\"$no\">";
		print "[$no] <b style=\"color:$subCol\">$sub</b> ";
		print "$nam - $dat <font color=\"$subCol\">【$hos】</font>\n";
		print "<dd><span style=\"font-size:11px\">$com</span>\n";
	}
	close(IN);

	print <<EOM;
<dt><hr>
</dl>
</form>
</body>
</html>
EOM
	exit;
}

#-------------------------------------------------
#  修正フォーム
#-------------------------------------------------
sub edit_form {
	$com =~ s/<br>/\n/g;
	if (!$url) { $url = "http://"; }

	&header;
	print <<EOM;
<form action="$script" method="post">
<input type="hidden" name="mode" value="admin">
<input type="hidden" name="pass" value="$in{'pass'}">
<input type="submit" value="&lt; 前画面へ">
</form>
<ul>
<li>変更する部分のみ修正して送信ボタンを押してください。
</ul>
<form action="$script" method="post">
<input type="hidden" name="mode" value="admin">
<input type="hidden" name="job" value="edit2">
<input type="hidden" name="no" value="$in{'no'}">
<input type="hidden" name="pass" value="$in{'pass'}">
<table cellpadding="1" cellspacing="1">
<tr>
  <td><b>おなまえ</b></td>
  <td><input type="text" name="name" size="28" value="$nam"></td>
</tr>
<tr>
  <td><b>Ｅメール</b></td>
  <td><input type="text" name="email" size="28" value="$eml"></td>
</tr>
<tr>
  <td><b>タイトル</b></td>
  <td><input type="text" name="sub" size="36" value="$sub"></td>
</tr>
<tr>
  <td><b>参照先</b></td>
  <td><input type="text" name="url" size="50" value="$url"></td>
</tr>

<tr>
  <td colspan=2>
    <b>メッセージ</b><br>
    <textarea name="comment" cols="56" rows="7">$com</textarea><br>
<input type="submit" value="送信する"><input type="reset" value="リセット">
  </td>
</tr>
</table>
</form>
</body>
</html>
EOM
	exit;
}

#-------------------------------------------------
#  留意事項
#-------------------------------------------------
sub howto {
	&header;
	print <<EOM;
<div align="center">
<table border="1" cellpadding="10" width="75%">
<tr><td class="tbl">
<h3 align="center">留意事項</h3>
<ol>
<li>この掲示板は<b>クッキー対応</b>です。一度記事を投稿いただくと、おなまえ、Ｅメール、ＵＲＬ、削除キーの情報は2回目以降は自動入力されます。（ただし利用者のブラウザがクッキー対応の場合）<br><br>
<li>投稿記事には、<b>タグは一切使用できません。</b><br><br>
<li>記事を投稿する上での必須入力項目は<b>「おなまえ」</b>と<b>「メッセージ」</b>です。Ｅメール、ＵＲＬ、題名、削除キーは任意です。<br><br>
<li>記事には、<b>半角カナは一切使用しないで下さい。</b>文字化けの原因となります。<br><br>
<li>記事の投稿時に<b>「削除キー」</b>にパスワード（英数字で8文字以内）を入れておくと、その記事は次回<b>削除キー</b>によって削除することができます。<br><br>
<li>記事の保持件数は<b>最大$max件</b>です。それを超えると古い順に自動削除されます。<br><br>
<li>既存の記事に簡単に<b>「返信」</b>することができます。各記事にある<b>「返信」</b>のリンク部を押すと投稿フォームが返信用となります。<br><br>
<li>過去の投稿記事から<b>「キーワード」によって簡易検索ができます。</b>トップメニューの<a href="$script?mode=find">「ワード検索」</a>のリンクをクリックすると検索モードとなります。<br><br>
<li>管理者が著しく不利益と判断する記事や他人を誹謗中傷する記事は予\告なく削除することがあります。
</ol>
</td></tr></table>
<p>
<form>
<input type="button" value="掲示板に戻る" onclick="history.back()">
</form>
</div>
</body>
</html>
EOM
	exit;
}

#-------------------------------------------------
#  ユーザ記事削除
#-------------------------------------------------
sub usrdel {
	# 投稿チェック
	if ($postonly && !$post_flag) { &error("不正なアクセスです"); }
	if ($baseUrl) { &refCheck; }
	if ($in{'no'} eq '' || $in{'pwd'} eq '') {
		&error("削除Noまたは削除キーが入力モレです");
	}

	local($flag,$match,@data);
	eval 'flock(DAT, 2);';
	open(DAT,"+< $logfile") || &error("Open Error: $logfile");
	while (<DAT>) {
		local($no,$dat,$nam,$eml,$sub,$com,$url,$hos,$pw) = split(/<>/);

		if ($in{'no'} == $no) {
			$flag = 1;
			if (!$pw) { $flag = 2; last; }
			$match = &decrypt($in{'pwd'}, $pw);
			next;
		}
		push(@data,$_);
	}

	# 判定
	if ($flag == 2) { &error("削除キーが設定されていません"); }
	elsif (!$flag) { &error("該当記事が見当たりません"); }
	elsif ($match != 1) { &error("削除キーが違います"); }

	# ログを更新
	seek(DAT, 0, 0);
	print DAT @data;
	truncate(DAT, tell(DAT));
	close(DAT);

	# 完了メッセージ
	&message("記事を削除しました");
}

#-------------------------------------------------
#  フォームデコード
#-------------------------------------------------
sub decode {
	local($buf);
	if ($ENV{'REQUEST_METHOD'} eq "POST") {
		$post_flag = 1;
		if ($ENV{'CONTENT_LENGTH'} > $maxData) {
			&error("投稿量が大きすぎます");
		}
		read(STDIN, $buf, $ENV{'CONTENT_LENGTH'});
	} else {
		$post_flag = 0;
		$buf = $ENV{'QUERY_STRING'};
	}

	undef(%in); $page = 0;
	foreach ( split(/&/, $buf) ) {
		local($key, $val) = split(/=/);
		$val =~ tr/+/ /;
		$val =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("H2", $1)/eg;

		# S-JISコード変換
		&jcode'convert(*val, "sjis", "", "z");

		# エスケープ
		$val =~ s/&/&amp;/g;
		$val =~ s/"/&quot;/g;
		$val =~ s/</&lt;/g;
		$val =~ s/>/&gt;/g;
		$val =~ s/\r\n/<br>/g;
		$val =~ s/\r/<br>/g;
		$val =~ s/\n/<br>/g;

		# ページ繰り越し
		if ($key =~ /^page(\d+)$/) {
			$page = $1;
		}

		$in{$key} .= "\0" if (defined($in{$key}));
		$in{$key} .= $val;
	}
	$mode = $in{'mode'};

	# タイムゾーン設定
	$ENV{'TZ'} = "JST-9";

	$headflag = 0;
}

#-------------------------------------------------
#  HTMLヘッダ
#-------------------------------------------------
sub header {
	if ($headflag) { return; }

	print "Content-type: text/html\n\n";
	print <<"EOM";
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html lang="ja">
<head>
<META HTTP-EQUIV="Content-type" CONTENT="text/html; charset=Shift_JIS">
<META HTTP-EQUIV="Content-Style-Type" content="text/css">
<STYLE TYPE="text/css">
<!--
body,td,th { font-size:$bSize; font-family:$bFace }
.tbl { background-color:#ffffff; color:#000000; }
-->
</STYLE>
<title>$title</title></head>
$body
EOM
	$headflag = 1;
}

#-------------------------------------------------
#  エラー処理
#-------------------------------------------------
sub error {
	&header;
	print <<EOM;
<div align="center">
<table border=1 cellpadding=18 cellspacing=0 width="450">
<tr><td class="tbl" align="center">
<h3>ERROR !</h3>
<font color="#dd0000">$_[0]</font>
<p>
<form>
<input type="button" value="前画面に戻る" onclick="history.back()">
</form>
</td></tr></table>
</div>
</body>
</html>
EOM
	exit;
}

#-------------------------------------------------
#  クッキー発行
#-------------------------------------------------
sub set_cookie {
	local(@cook) = @_;
	local($gmt, $cook, @t, @m, @w);

	@t = gmtime(time + 60*24*60*60);
	@m = ('Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec');
	@w = ('Sun','Mon','Tue','Wed','Thu','Fri','Sat');

	# 国際標準時を定義
	$gmt = sprintf("%s, %02d-%s-%04d %02d:%02d:%02d GMT",
			$w[$t[6]], $t[3], $m[$t[4]], $t[5]+1900, $t[2], $t[1], $t[0]);

	# URLエンコード
	foreach (@cook) {
		s/(\W)/sprintf("%%%02X", unpack("C", $1))/eg;
		$cook .= "$_<>";
	}

	# 格納
	print "Set-Cookie: ASKA_BBS=$cook; expires=$gmt\n";
}

#-------------------------------------------------
#  クッキー取得
#-------------------------------------------------
sub get_cookie {
	local($key, $val, *cook);

	# クッキーを取得
	$cook = $ENV{'HTTP_COOKIE'};

	# 該当IDを取り出す
	foreach ( split(/;/, $cook) ) {
		($key, $val) = split(/=/);
		$key =~ s/\s//g;
		$cook{$key} = $val;
	}

	# データをURLデコードして復元
	foreach ( split(/<>/, $cook{'ASKA_BBS'}) ) {
		s/%([0-9A-Fa-f][0-9A-Fa-f])/pack("H2", $1)/eg;

		push(@cook,$_);
	}
	return (@cook);
}

#-------------------------------------------------
#  crypt暗号
#-------------------------------------------------
sub encrypt {
	local($in) = @_;
	local($salt, $enc, @s);

	@s = ('a'..'z', 'A'..'Z', '0'..'9', '.', '/');
	srand;
	$salt = $s[int(rand(@s))] . $s[int(rand(@s))];
	$enc = crypt($in, $salt) || crypt ($in, '$1$' . $salt);
	$enc;
}

#-------------------------------------------------
#  crypt照合
#-------------------------------------------------
sub decrypt {
	local($in, $dec) = @_;

	local($salt) = $dec =~ /^\$1\$(.*)\$/ && $1 || substr($dec, 0, 2);
	if (crypt($in, $salt) eq $dec || crypt($in, '$1$' . $salt) eq $dec) {
		return (1);
	} else {
		return (0);
	}
}

#-------------------------------------------------
#  メール送信
#-------------------------------------------------
sub mail_to {
	local($msub,$mbody,$mcom,$email);

	# メールタイトルを定義
	$msub = "[$title : $no] $in{'sub'}";

	# 本文の改行・タグを復元
	$mcom = $in{'comment'};
	$mcom =~ s/<br>/\n/g;
	$mcom =~ s/&lt;/＜/g;
	$mcom =~ s/&gt;/＞/g;
	$mcom =~ s/&quot;/”/g;
	$mcom =~ s/&amp;/＆/g;

	$mbody = "$titleに以下の投稿がありました。\n\n";
	$mbody .= "Date : $date\n";
	$mbody .= "Host : $host\n";
	$mbody .= "Agent: $ENV{'HTTP_USER_AGENT'}\n\n";
	$mbody .= "名前 : $in{'name'}\n";
	$mbody .= "email: $in{'email'}\n";
	$mbody .= "題名 : $in{'sub'}\n";
	$mbody .= "参照 : $in{'url'}\n" if ($in{'url'});
	$mbody .= "\n$mcom\n";

	# 題名をBASE64化
	$msub = &base64($msub);

	# メールアドレスがない場合は管理者アドレスに置き換え
	if ($in{'email'} eq "") { $email = $mailto; }
	else { $email = $in{'email'}; }

	# sendmail送信
	open(MAIL,"| $sendmail -t -i") || &error("メール送信失敗");
	print MAIL "To: $mailto\n";
	print MAIL "From: $email\n";
	print MAIL "Subject: $msub\n";
	print MAIL "MIME-Version: 1.0\n";
	print MAIL "Content-type: text/plain; charset=iso-2022-jp\n";
	print MAIL "Content-Transfer-Encoding: 7bit\n";
	print MAIL "X-Mailer: $ver\n\n";
	foreach ( split(/\n/, $mbody) ) {
		&jcode'convert(*_, 'jis', 'sjis');
		print MAIL $_, "\n";
	}
	close(MAIL);
}

#-------------------------------------------------
#  自動リンク
#-------------------------------------------------
sub auto_link {
	$_[0] =~ s/([^=^\"]|^)(https?\:[\w\.\~\-\/\?\&\=\@\;\#\:\%]+)/$1<a href=\"$2\" target=\"_blank\">$2<\/a>/g;
}

#-------------------------------------------------
#  REFチェック
#-------------------------------------------------
sub refCheck {
	local($ref) = $ENV{'HTTP_REFERER'};
	$ref =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("H2", $1)/eg;
	$baseUrl =~ s/(\W)/\\$1/g;
	if ($ref && $ref !~ /$baseUrl/i) { &error("不正なアクセスです"); }
}

#-------------------------------------------------
#  BASE64変換
#-------------------------------------------------
#	とほほのWWW入門で公開されているルーチンを参考にしました。
#	http://tohoho.wakusei.ne.jp/
sub base64 {
	local($sub) = @_;
	&jcode'convert(*sub, 'jis', 'sjis');

	$sub =~ s/\x1b\x28\x42/\x1b\x28\x4a/g;
	$sub = "=?iso-2022-jp?B?" . &b64enc($sub) . "?=";
	$sub;
}
sub b64enc {
	local($ch)="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
	local($x, $y, $z, $i);
	$x = unpack("B*", $_[0]);
	for ($i=0; $y=substr($x,$i,6); $i+=6) {
		$z .= substr($ch, ord(pack("B*", "00" . $y)), 1);
		if (length($y) == 2) {
			$z .= "==";
		} elsif (length($y) == 4) {
			$z .= "=";
		}
	}
	$z;
}

#-------------------------------------------------
#  入室画面
#-------------------------------------------------
sub enter {
	&header;
	print <<EOM;
<div align="center">
<h4>パスワードを入力して下さい</h4>
<form action="$script" method="post">
<input type="hidden" name="mode" value="admin">
<input type="password" name="pass" size="12">
<input type="submit" value=" 認証 "></form>
</div>
<script language="javascript">
<!--
self.document.forms[0].pass.focus();
//-->
</script>
</body>
</html>
EOM
	exit;
}

#-------------------------------------------------
#  メッセージ表示
#-------------------------------------------------
sub message {
	&header;
	print <<EOM;
<div align="center">
<font size="+1">$_[0]</font>
<p>
<form action="$script" method="post">
EOM

	if ($in{'pass'} ne "") {
		print "<input type=hidden name=pass value=\"$in{'pass'}\">\n";
		print "<input type=hidden name=mode value=admin>\n";
	}

	print <<EOM;
<input type="submit" value="初期画面へ戻る">
</form>
</div>
</body>
</html>
EOM
	exit;
}

#-------------------------------------------------
#  禁止ワードチェック
#-------------------------------------------------
sub no_wd {
	local($flg);
	foreach ( split(/,/, $no_wd) ) {
		if (index("$in{'name'} $in{'sub'} $in{'comment'}",$_) >= 0) {
			$flg = 1; last;
		}
	}
	if ($flg) { &error("禁止ワードが含まれています"); }
}

#-------------------------------------------------
#  日本語チェック
#-------------------------------------------------
sub jp_wd {
	local($sub, $com, $mat1, $mat2, $code1, $code2);
	$sub = $in{'sub'};
	$com = $in{'comment'};
	if ($sub) {
		($mat1, $code1) = &jcode'getcode(*sub);
	}
	($mat2, $code2) = &jcode'getcode(*com);
	if ($code1 ne 'sjis' && $code2 ne 'sjis') {
		&error("題名又はコメントに日本語が含まれていません");
	}
}

#-------------------------------------------------
#  URL個数チェック
#-------------------------------------------------
sub urlnum {
	local($com) = $in{'comment'};
	local($num) = ($com =~ s|(https?://)|$1|ig);
	if ($num > $urlnum) {
		&error("コメント中のURLアドレスは最大$urlnum個までです");
	}
}

#-------------------------------------------------
#  チェックモード
#-------------------------------------------------
sub check {
	&header;
	print <<EOM;
<h2>Check Mode</h2>
<ul>
EOM

	# ログファイル
	if (-e $logfile) {
		print "<li>LOGパス：OK\n";
		if (-r $logfile && -w $logfile) {
			print "<li>LOGパーミッション：OK\n";
		} else {
			print "<li>LOGパーミッションが不正です。\n";
		}
	} else {
		print "<li>LOGのパスが不正です：NG → $logfile\n";
	}

	print <<EOM;
</ul>
</body>
</html>
EOM
	exit;
}

