NonSoft

掲示板CGIのサンプル(perl)


 サンプル(ソース/コード)
#!/usr/local/bin/perl
#---------------------------------------------------------------------
# 以下の項目はプロバイダのディスク容量に合わせて設定してください。
# SIDの数×投稿メッセージの保存AX件数×(本文のMAX文字数+
# その他の項目のMAX文字数×11)が安全範囲です。
# 普通は、SIDの数×投稿メッセージの保存MAX件数×500バイトで
# 十分でしょう。(掲示板荒らしに注意が必要ですが...)
#
# 投稿メッセージの保存MAX件数
$save_maxmsg = 1000;
# 本文のMAX文字数
$max_length = 6000;
# その他の項目のMAX文字数
$etc_length = 200;

#---------------------------------------------------------------------
# ファイル名:NONbbs.cgi:のん掲示板 Ver1.06
#
# 更新履歴:
# 2002.01.01 Ver1.00 初版。              作成者(のん)
# 2002.01.02 Ver1.01 ゴミとして残っている
#                    返信メッセージを削除する処理追加 変更者(のん)
# 2002.03.02 Ver1.02 コメント修正           変更者(のん)
# 2002.03.09 Ver1.03 メンバーパスワードをクッキー設定
#                    しない様に変更し、セキュリティー
#                    を高める             変更者(のん)
# 2002.03.10 Ver1.04 HTMLテンプレート不正指定禁止 変更者(のん)
# 2002.03.21 Ver1.05 クロスサイトスクリプティング対応 変更者(のん)
# 2002.03.25 Ver1.06 クロスサイトスクリプティング対応 変更者(のん)
# 2002.03.25 Ver1.06 open関数でのコマンド実行の禁止  変更者(のん)
#
# Copyright (c) 2001 のん
# 著作権  :著作権は放棄しません。
#       ファイル名の変更は禁止します。
#       使用・改造・再配布は制限しませんが、
#       改造したものを配布する場合は更新履歴をいれてください。
#
# 開発目標 :中小規模の汎用的(不特定用途対応)な掲示板を
#       目指しています。
#
# 機能概要 :概略としては以下の機能があります。
#       1.画面レイアウトを自由(ある程度)に変更できます。
#         (例)一覧表示、入力全項目表示
#         画面レイアウト用のHTMLテンプレートにより画面表示
#         するため、HTMLの変更によって画面レイアウト変更が
#         ある程度、簡単に可能です。
#         この掲示板の仕様を理解する必要はありますがPerlの
#         プログラミング知識はいりません。
#         (私もPerlが得意な訳ではありませんが...)
#         Perlをご存知の方は機能追加が出来ます。
#       2.入力する項目を自由(ある程度)に選べます。
#         (1)タイトル
#         (2)名前
#         (3)年齢
#         (4)性別
#         (5)Eメール
#         (6)URL
#         (7)文字の色
#         (8)背景の色
#         (9)削除キー
#        (10)本文
#        (11)その他1
#        (12)その他2
#       3.トピック(最初の投稿)毎に返信を表示します。
#       4.1ページに表示するトピック件数をHTMLテンプレート
#         で変更出来ます。
#       5.SIDという単位で掲示板データを管理しますので、この
#         プログラム一つでタイトルの異なる複数の掲示板を運営出
#         来ます。
#       6.掲示板の基本機能として以下の機能を備えています。
#        (1)新規登録:新規トピックとして登録します。
#        (2)返信表示:(Re:)形式で入力項目を表示します。
#        (3)返信登録:返信表示したトピックに返信します。
#        (4)削除  :削除キーが一致した時に削除します。
#                トピックを削除すると返信登録したデータ
#                全てを削除します。
#       7.管理者機能として以下の機能を備えています。
#        (1)バックアップ
#           掲示板データをダウンロードします。
#        (2)管理者パスワード変更
#        (3)SID追加
#        (4)SID削除
#           運営していた掲示板が一時的にアクセス出来無くなり
#           ます。
#        (5)メンバー追加
#        (6)メンバー削除
#           メンバーを登録すると登録したメンバーのみ掲示板に
#           アクセス出来ます。
#           メンバー登録した掲示板にアクセスするとメンバーパ
#           スワード入力画面が表示され認証します。
#           認証にはクッキーを使用しています。
#       8.エラー表示
#         認証エラー等でエラーメッセージを表示しますが、セキュ
#         リティーのため、詳しいメッセージは表示しません。
#         ...と言うか面倒だった!?
#
# 関連ファイル  :この掲示板はHTMLテンプレートファイルが1つ
#          以上必要です。このCGI単体では動きません。
#          HTMLテンプレートファイルは"NONbbs.cgi"と同じ
#          ディレクトリに存在し、拡張子は".htm"または".html"
#          です。
#
# 管理ファイル  :(1)SIDファイル(data/sid_NONbbs.txt)
#             SIDを1行単位で複数記述します。
#             各行に改行が必要です。
#             管理者機能でも追加/削除出来ます。
#          (2)管理者パスワードファイル(data/passwd_NONbss.txt)
#             管理者パスワード1件のみを1行に記述します。
#             管理者パスワードの後ろに改行が必要です。
#             管理者機能でも変更出来ます。
#          (3)メンバーファイル(data/xxx_menber.txt)
#             xxxの部分は、SIDです。
#             メンバーを1行単位で複数記述します。
#             各行に改行が必要です。
#             管理者機能でも追加/削除出来ます。
#          (4)掲示板データファイル(data/xxx_NONbbs.txt)
#             xxxの部分は、SIDです。
#             掲示板データファイルです。
#             バックアップ機能でこのファイルをバックアップ
#             します。
#
# インストール方法:NONbbs.cgiと関連ファイルをCGIが動作するディレクトリ
#          に置きます。(サーバーがUNIXの場合は、
#          ".cgi"ファイルと".pl"ファイルに実行権を与えます。)
#          NONbbs.cgiと同じディレクトリにdataディレクトリを作成し
#          管理ファイルを置きます。
#          NONbbs.cgiをURL指定して起動するとデフォルトSIDで
#          動作します。
#
# 起動パラメータ :html---HTMLテンプレート(default="NONbbs.html")
#          sid----SID(default="etc")
#          sname--掲示板タイトル(default="掲示板")
#          sno----表示先頭トピック番号(default="0")
#
#   CGIの起動方法(例):
#    1行目:<form name="bbs"
#    2行目: action="http://xxx.xxx.xxx/NONbbs/NONbbs.cgi"
#    3行目: method="POST" target="_blank">
#    4行目:<input type="hidden" name="sid" value="etc">
#    5行目:<input type="hidden" name="sname" value="掲示板タイトル">
#    6行目:<a href="javaScript:document.bbs.submit()">掲示板タイトル</a>
#    7行目:</form>
#
#     (CGIの起動方法(例)の説明)
#      1〜3行目で、起動するCGIを指定しています。
#      4行目で、掲示板のSIDを指定しています。
#      5行目で、掲示板のタイトルを指定しています。
#      6行目で、掲示板へのリンクを指定しています。
#
# 関連ドキュメント:掲示板カスタマイズ用のマニュアルを作成予定です。
#          HTMLテンプレートの作成方法を記述します。
#
# 関連プログラム :(1)カウンタを表示する場合は、NONcount.cgiを
#             使用する事が出来ます。
#          (2)この掲示板にGIFイメージを表示する場合は、
#             NONimg.cgiを使用する事が出来ます。
#          関連プログラムについては、関連プログラムの仕様書を
#          参照してください。
#
#---------------------------------------------------------------------
#---------------------------------------------------------------------
# リンク/ボタンをクリックした時の入力情報を取得
local($str,$value,$variable,@part);
if($ENV{'REQUEST_METHOD'} eq "POST") {
	read(STDIN, $str, $ENV{'CONTENT_LENGTH'});
	$str = $str . "&" . $ENV{'QUERY_STRING'};
}elsif($ENV{'REQUEST_METHOD'} eq "GET") {
	$str = $ENV{'QUERY_STRING'};
}
$ip = $ENV{REMOTE_ADDR};

# ボタンをクリックした時の入力情報を個別に定義
$fs = "	";
if( $str ne "" ){
	%cgi = "";
	@part = split( '&' , $str );
	foreach $i (@part) {
		($variable,$value) = split('=',$i);
		$value =~ tr/+/ /;
		$value =~ s/%([0-9a-fA-F][0-9a-fA-F])/pack("C",hex($1))/eg;
#		&jcode'convert(*value, 'euc');
		$value =~ s/&/&amp;/g;
		$value =~ s/</&lt;/g;
		$value =~ s/>/&gt;/g;
		$value =~ s/"/&quot;/g;
		$value =~ s/'/&#39;/g;
		$value =~ s/\015\012/\012/g;
		$value =~ s/\015/\012/g;
		if( $cgi{$variable} eq "" ){
			$cgi{$variable} = $value;
		}else{
			$cgi{$variable} = $cgi{$variable} . $fs . $value;
		}
	}
	# 起動情報の個別定義
	$html = $cgi{'html'};
	$sid = $cgi{'sid'};
	$sname = $cgi{'sname'};
	$sno = $cgi{'sno'};

	# 入力情報の個別定義
	$title = $cgi{'title'};
	$name = $cgi{'name'};
	$age = $cgi{'age'};
	$sex = $cgi{'sex'};
	$email = $cgi{'email'};
	$url = $cgi{'url'};
	$fgcol = $cgi{'fgcol'};
	$bgcol = $cgi{'bgcol'};
	$delkey = $cgi{'delkey'};
	$msg = $cgi{'msg'};
	$etc1 = $cgi{'etc1'};
	$etc2 = $cgi{'etc2'};

	# メンバーパスワード情報の定義
	$mempswd = $cgi{'mempswd'};

	# ボタン情報の個別定義
	$save_btn = $cgi{'save'};
	$del_btn = $cgi{'delete'};
	$res_save_btn = $cgi{'res_save'};
	$res_dsp_btn = $cgi{'res_dsp'};
	$selno = $cgi{'selno'};
	$manage_btn = $cgi{'manage'};

	# 管理者機能情報の個別定義
	$mngpswd = $cgi{'mngpswd'};
	$mngpswd1 = $cgi{'mngpswd1'};
	$mngpswd2 = $cgi{'mngpswd2'};
	$chgval = $cgi{'chgval'};
	$mngmenu = $cgi{'mngmenu'};
}
# デフォルト値の定義
if( $html eq "" ){
	$html = "NONbbs.html";
}
if( $sid eq "" ){
	$sid = "etc";
}
if( $sname eq "" ){
	$sname = "掲示板";
}
if( $sno eq "" ){
	$sno = 0;
}

# 前後のホワイトスペース削除
$title=&bbs_trim($title);
$name=&bbs_trim($name);
$age=&bbs_trim($age);
$sex=&bbs_trim($sex);
$email=&bbs_trim($email);
$url=&bbs_trim($url);
$fgcol=&bbs_trim($fgcol);
$bgcol=&bbs_trim($bgcol);
$delkey=&bbs_trim($delkey);
$msg=&bbs_trim($msg);
$etc1=&bbs_trim($etc1);
$etc2=&bbs_trim($etc2);

# 各項目の桁数オーバー部分の削除
$title = substr($title,0,$etc_length);
$name = substr($name,0,$etc_length);
$age = substr($age,0,$etc_length);
$sex = substr($sex,0,$etc_length);
$email = substr($email,0,$etc_length);
$url = substr($url,0,$etc_length);
$fgcol = substr($fgcol,0,$etc_length);
$bgcol = substr($bgcol,0,$etc_length);
$delkey = substr($delkey,0,$etc_length);
$msg = substr($msg,0,$max_length);
$etc1 = substr($etc1,0,$etc_length);
$etc2 = substr($etc2,0,$etc_length);

# ファイル名をここに定義
$bbs_cgi="NONbbs.cgi";
$bbs_tmp = "data/${sid}_NONbbs.tmp";
$bbs_lock = "data/${sid}_NONbbs.loc";
$bbs_file = "data/${sid}_NONbbs.txt";
$mem_file = "data/${sid}_menber.txt";
$psw_file = "data/passwd_NONbss.txt";
$sid_file = "data/sid_NONbss.txt";

# 同時アクセスを防止するためのロック
#sub sigexit { rmdir($bbs_lock); exit(0); }
$SIG{'PIPE'} = $SIG{'INT'} = $SIG{'HUP'} = $SIG{'QUIT'} = $SIG{'TERM'} = "sigexit";
for( $ded=0 ; $ded < 60 &&
	mkdir("$bbs_lock",0777)==0 ; $ded++ ){
	sleep(1);
}

# システムパスワード読み込み
$sys_passwd = "h;ea;o97qhfclsi6o132r.ktdcaw762lhalsh/i";
open( PSW , "$psw_file" );
while( <PSW> ){
	chop($_);
	$sys_passwd = $_;
	last;
}
close( PSW );

# 管理機能
$dspmsg = "";
if( $manage_btn ne "" && $mngmenu ne "" ){
	if( $mngmenu eq "backup" ){
		# バックアップ
		$dspmsg = backup_func();
	}elsif( $mngmenu eq "pswdchg" ){
		# パスワード変更
		$dspmsg = pswdchg_func();
	}elsif( $mngmenu eq "sidadd" ){
		# SID追加
		$dspmsg = sidadd_func();
	}elsif( $mngmenu eq "siddel" ){
		# SID削除
		$dspmsg = siddel_func();
	}elsif( $mngmenu eq "memadd" ){
		# メンバー追加
		$dspmsg = memadd_func();
		# メンバーパスワードのクッキークリア
		print "Set-Cookie: NONbbsMemberPassword=\n";
	}elsif( $mngmenu eq "memdel" ){
		# メンバー削除
		$dspmsg = memdel_func();
		# メンバーパスワードのクッキークリア
		print "Set-Cookie: NONbbsMemberPassword=\n";
	}
	# メッセージ表示
	&msgdsp_func();
	# 終了処理
	&end_func();
	exit;
}

# メンバーパスワード読み込み
@mem_passwd = ("");
open( MEM , "$mem_file" );
while( <MEM> ){
	chop($_);
	@mem_passwd = ($_,@mem_passwd);
}
close( MEM );

# メンバーパスワードチェック
$dspmsg = "";
$mem_check = 1;
if( @mem_passwd[0] ne "" ){
	if( $mempswd ne "" ){
		@mem_passwd = ($sys_passwd,@mem_passwd);
		foreach(@mem_passwd){
			if( $_ ne "" && $_ eq $mempswd ){
				# メンバーパスワードのクッキー設定
				#print "Set-Cookie: NONbbsMemberPassword=$sid\n";
				print "Set-Cookie: NONbbsMemberPassword$sid=$sid\n";
				# 掲示板の表示
				&reload_func();
				# 終了処理
				&end_func();
				exit;
			}else{
				$mem_check = 2;
			}
		}
	}
	$cookie = $ENV{'HTTP_COOKIE'};
	if( index($cookie, "NONbbsMemberPassword$sid") >=0 ){
		$mem_check = 0;
	}

	if( $mem_check != 0 ){
		$dspmsg = "$dspmsgメンバー制の掲示板です。<br>\n";
		if( $mem_check == 2 ){
			$dspmsg = "$dspmsg <font color=\"#FF0000\">\n";
			$dspmsg = "$dspmsg アクセスが拒否されました。\n";
			$dspmsg = "$dspmsg </font><br>\n";
		}
		$dspmsg = "$dspmsg メンバーパスワードを入力してください。<br>\n";
		$dspmsg = "$dspmsg メンバーパスワード ";
		$dspmsg = "$dspmsg <input type=\"password\" name=\"mempswd\" value=\"\" ";
		$dspmsg = "$dspmsg size=10 maxlength=10>\n";

		# メンバーパスワードのクッキークリア
		print "Set-Cookie: NONbbsMemberPassword=\n";

		# メッセージ表示
		&msgdsp_func();

		# 終了処理
		&end_func();
		exit;
	}
}

$dspmsg = "";
# システムIDチェック
$sid_flg = 0;
open( SID , "$sid_file" );
while( <SID> ){
	chop($_);
	if( $sid eq $_ ){
		$sid_flg = 1;
		last;
	}
}
close( SID );

# HTMLファイル名の不正指定チェック
$html_flg = 0;
$index = rindex($html,"/");
$index2 = rindex($html,"|");
$index3 = rindex($html,"<");
$index4 = rindex($html,">");
if( $index < 0 && $index2 < 0 &&  $index3 < 0 && $index4 < 0 ){
	$index = rindex($html,".");
	if( $index >= 0 ){
		$ext = substr( $html , $index );
		if( $ext eq ".htm" || $ext eq ".html" ){
			$html_flg = 1;
		}
	}
}

if( $sid_flg == 0 || $html_flg == 0 ){
	$dspmsg = "<font color=\"#FF0000\">アクセスが拒否されました。</font>";
}else{
	if( $save_btn eq "" && $res_save_btn eq "" && $del_btn eq "" ){
		# 表示系
		if( $res_dsp_btn eq "" ){
			# リスト選択番号のクリア
			$selno = "";
		}
		# 掲示板の表示
		&dsp_func();
		# 終了処理
		&end_func();
		exit;
	}else{
		# 更新系
		if( $save_btn ne "" ){
			# 新規投稿
			# メッセージ選択No.のクリア
			$selno = "";
			# メッセージ表示先頭No.のクリア
			$sno = "";
			$dspmsg = &save_func();
		}elsif( $res_save_btn ne "" ){
			# 返信投稿
			$dspmsg = &save_func();
		}elsif( $del_btn ne "" ){
			# 削除
			$dspmsg = &del_func();
		}
		if( $dspmsg eq "" ){
			# 掲示板の表示
			&reload_func();
			# 終了処理
			&end_func();
			exit;
		}
	}
}

# メッセージ表示
&msgdsp_func();
# 終了処理
&end_func();
exit;

# 終了処理 ----------------------------------------------------------
sub end_func{
	# 中間ファイルおよびロックファイルを削除する
	unlink("$bbs_tmp");
	rmdir("$bbs_lock");
	rmdir("$bbs_lock");
	exit;
}

# メッセージ表示 ----------------------------------------------------
sub msgdsp_func{
	if( $dspmsg ne "" ){
		# メッセージ表示
		print "Content-type: text/html\n\n";
		print <<EOF;
<html><head>
<title>$sname メッセージ</title>
</head>
<body bgcolor="#CCEEEE" text="#000000">
<form name="bbs_dsp" action="$bbs_cgi" method="POST">
<input type="hidden" name="html" value="$html">
<input type="hidden" name="sid" value="$sid">
<input type="hidden" name="sname" value="$sname">
<input type="hidden" name="sno" value="$sno">
<input type="hidden" name="selno" value="$selno">
$dspmsg
<input type="submit" name="dsp" value="掲示板表\示">
</form>
<font color="#888888" size="2">
<form name="bbs_mng" action="$bbs_cgi" method="POST">
<input type="hidden" name="html" value="$html">
<input type="hidden" name="sid" value="$sid">
<input type="hidden" name="sname" value="$sname">
<input type="hidden" name="sno" value="$sno">
<input type="hidden" name="selno" value="$selno">
<hr>
管理機能\<select name="mngmenu" size="1">
<option value="backup">バックアップ</option>
<option value="pswdchg">パスワード変更</option>
<option value="sidadd">SID追加</option>
<option value="siddel">SID削除</option>
<option value="memadd">メンバー追加</option>
<option value="memdel">メンバー削除</option>
</select>
新パスワード<input type="password" name="mngpswd1" value="" size=5 maxlength=10>
<input type="password" name="mngpswd2" value="" size=5 maxlength=10>
SID/メンバー<input type="text" name="chgval" value="" size=5 maxlength=10>
管理者パスワード<input type="password" name="mngpswd" value="" size=5 maxlength=10>
<input type="submit" name="manage" value="実 行">
<hr>
</form>
</font>
</body>
</html>
EOF
	}
}

# 掲示板の再表示 ----------------------------------------------------
sub reload_func{
	# 掲示板の表示
	print "Content-type: text/html\n\n";
	print <<EOF;
<html><head></head>
<body bgcolor="#CCEEEE" text="#000000" onLoad="document.bbs_dsp.submit()">
<form name="bbs_dsp" action="$bbs_cgi" method="POST">
<input type="hidden" name="html" value="$html">
<input type="hidden" name="sid" value="$sid">
<input type="hidden" name="sname" value="$sname">
<input type="hidden" name="sno" value="$sno">
<input type="hidden" name="selno" value="$selno">
</form>
</body>
</html>
EOF
}

# メンバー追加 ------------------------------------------------------
sub memadd_func{
	if( $mngpswd eq $sys_passwd && $chgval ne "" ){
		open( TMPOUT , ">$bbs_tmp" );
		open( MEMIN , "$mem_file" );
		print TMPOUT <MEMIN>;
		print TMPOUT "$chgval\n";
		close( MEMIN );
		close( TMPOUT );

		open( MEMOUT , ">$mem_file" );
		open( TMPIN , "$bbs_tmp" );
		print MEMOUT <TMPIN>;
		close( TMPIN );
		close( MEMOUT );
	}else{
		return "<font color=\"#FF0000\">メンバー追加失敗</font>";
	}
	return "成功";
}

# メンバー削除 ------------------------------------------------------
sub memdel_func{
	local $flg=0;
	if( $mngpswd eq $sys_passwd && $chgval ne "" ){
		open( TMPOUT , ">$bbs_tmp" );
		open( MEMIN , "$mem_file" );
		while( <MEMIN> ){
			chop($_);
			if( $chgval ne $_ ){
				print TMPOUT "$_\n";
			}else{
				$flg=1;
			}
		}
		close( MEMIN );
		close( TMPOUT );
		open( MEMOUT , ">$mem_file" );
		open( TMPIN , "$bbs_tmp" );
		print MEMOUT <TMPIN>;
		close( TMPIN );
		close( MEMOUT );
	}
	if( $flg == 0 ){
		return "<font color=\"#FF0000\">メンバー削除失敗</font>";
	}
	return "成功";
}

# SID追加 --------------------------------------------------------
sub sidadd_func{
	if( $mngpswd eq $sys_passwd && $chgval ne "" ){
		open( TMPOUT , ">$bbs_tmp" );
		open( SIDIN , "$sid_file" );
		print TMPOUT <SIDIN>;
		print TMPOUT "$chgval\n";
		close( SIDIN );
		close( TMPOUT );

		open( SIDOUT , ">$sid_file" );
		open( TMPIN , "$bbs_tmp" );
		print SIDOUT <TMPIN>;
		close( TMPIN );
		close( SIDOUT );
	}else{
		return "<font color=\"#FF0000\">SID追加失敗</font>";
	}
	return "成功";
}

# SID削除 --------------------------------------------------------
sub siddel_func{
	local $flg=0;
	if( $mngpswd eq $sys_passwd && $chgval ne "" ){
		open( TMPOUT , ">$bbs_tmp" );
		open( SIDIN , "$sid_file" );
		while( <SIDIN> ){
			chop($_);
			if( $chgval ne $_ ){
				print TMPOUT "$_\n";
			}else{
				$flg=1;
			}
		}
		close( SIDIN );
		close( TMPOUT );
		open( SIDOUT , ">$sid_file" );
		open( TMPIN , "$bbs_tmp" );
		print SIDOUT <TMPIN>;
		close( TMPIN );
		close( SIDOUT );
	}
	if( $flg == 0 ){
		return "<font color=\"#FF0000\">SID削除失敗</font>";
	}
	return "成功";
}

# バックアップ処理 --------------------------------------------------
sub backup_func{
	if( $mngpswd eq $sys_passwd ){
		print "Content-type: data/text\n\n";
		open( BBSIN , "$bbs_file" );
		print <BBSIN>;
		close( BBSIN );
	}else{
		return "<font color=\"#FF0000\">バックアップ失敗</font>";
	}
	return "";
}

# パスワード変更処理 ------------------------------------------------
sub pswdchg_func{
	if( $mngpswd eq $sys_passwd && $mngpswd1 eq $mngpswd2 && $mngpswd2 ne "" ){
		open( PSW , ">$psw_file" );
		print PSW "$mngpswd1\n";
		close( PSW );
	}else{
		return "<font color=\"#FF0000\">パスワード変更失敗</font>";
	}
	return "成功";
}

# 投稿処理 ----------------------------------------------------------
sub save_func{
	# 追加投稿番号の取得
	local $no = &bbs_newno();

	# 親番号を設定
	local $parent = $no;
	# メッセージが選択されている場合は親番号を取得
	if( $selno ne "" ){
		local($no_p,$parent_p,
		$date_p,$ip_p,$title_p,$name_p,
		$age_p,$sex_p,$email_p,
		$url_p,$fgcol_p,$bgcol_p,
		$delkey_p,$msg_p,$etc1_p,
		$etc2_p) = bbs_read($selno);
		$parent = $parent_p;
	}

	# 年月日時分秒を出力する
	local($now)=time();
	local($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst)=localtime($now);
	$mon++;$year+=1900;
	local $date = sprintf("%04d年%02d月%02d日%02d時%02d分%02d秒",
		$year,$mon,$mday,$hour,$min,$sec);

	# メッセージを追加
	&bbs_write($no,$parent,$date,$ip,$title,$name,$age,$sex,$email,
	 $url,$fgcol,$bgcol,$delkey,$msg,$etc1,$etc2);
	return "";
}

# 削除処理 ----------------------------------------------------------
sub del_func{
	# メッセージが選択されている場合のみ削除
	if( $selno > 0 ){
		local($no_p,$parent_p,
		$date_p,$ip_p,$title_p,$name_p,
		$age_p,$sex_p,$email_p,
		$url_p,$fgcol_p,$bgcol_p,
		$delkey_p,$msg_p,$etc1_p,
		$etc2_p) = bbs_read($selno);

		# 削除キーが一致した場合のみメッセージを削除
		if( $delkey eq $delkey_p || $delkey eq $sys_passwd){
			&bbs_delete($selno);
		}else{
			return "<font color=\"#FF0000\">削除キーが誤っています。</font>";
		}
	}
	return "";
}

# 表示処理 ----------------------------------------------------------
sub dsp_func{
	print "Content-type: text/html\n\n";

	# 入力領域のクリア
	$title = "";
	$name = "";
	$age = "";
	$sex = "";
	$email = "";
	$url = "";
	$fgcol = "";
	$bgcol = "";
	$delkey = "";
	$msg = "";
	$etc1 = "";
	$etc2 = "";

	# メッセージが選択されている場合は取得
	if( $selno > 0 ){
		local($no_p,$parent_p,
		$date_p,$ip_p,$title_p,$name_p,
		$age_p,$sex_p,$email_p,
		$url_p,$fgcol_p,$bgcol_p,
		$delkey_p,$msg_p,$etc1_p,
		$etc2_p) = bbs_read($selno);
		# 入力領域の返信設定
		$title = "Re($no_p):$title_p";
		$msg =  "&gt;$msg_p";
		$msg =~ s/\n/\n&gt/gi;
		$msg =  "$msg\n";
	}

	# 掲示板の表示
	local $before_no = -1;
	local $next_no = -1;
	open( IN ,"$html");
	while( <IN> ){
		chop($_);
		if( m#<!--main_loop=(\d+)-->#i ){
			m/<!--main_loop=(\d+)-->/i && ($dsp_maxmsg = $1);

			local @line = ();
			local @line2 = ();
			local @cline = ();
			local $chikdlflag = 0;
			while( <IN> ){
				chop($_);
				if( m#<!--/main_loop-->#i ){
					last;
				}elsif( m#<!--res_loop-->#i ){
					$chikdlflag = 1;
					next;
				}elsif( m#<!--/res_loop-->#i ){
					$chikdlflag = 2;
					next;
				}
				if( $chikdlflag == 0 ){
					@line = (@line,"$_");
				}elsif( $chikdlflag == 1 ){
					@cline = (@cline,"$_");
				}elsif( $chikdlflag == 2 ){
					@line2 = (@line2,"$_");
				}
			}

			local @ba_rlist = bbs_getroot($sno,$dsp_maxmsg);
			local @rlist = ();
			local $cnt=0;
			foreach(@ba_rlist){
				$cnt++;
				if( $cnt == 1 ){
					# 前ページ設定
					$before_no = $_;
					next;
				}elsif( $cnt == 2 ){
					# 次ページ設定
					$next_no = $_;
					next;
				}
				@rlist = (@rlist,$_);
			}
			$sno = @rlist[0];
			foreach(@rlist){
				local($no,$parent,
				$date,$ip,$title,$name,
				$age,$sex,$email,
				$url,$fgcol,$bgcol,
				$delkey,$msg,$etc1,
				$etc2) = bbs_read($_);
#				$email = "<a href=\"mailto:$email\">$email</a>";
#				$url = "<a href=\"$url\">$url</a>";
				foreach(@line){
					local $l = &change_tg($_);
					$l =~ s/\n/<br>\n/gi;
					print $l , "\n";
				}

				# 返信データ変換
				local @clist = bbs_getresp($no);
				foreach(@clist){
					local($no,$parent,
					$date,$ip,$title,$name,
					$age,$sex,$email,
					$url,$fgcol,$bgcol,
					$delkey,$msg,$etc1,
					$etc2) = bbs_read($_);
#					$email = "<a href=mailto:\"$email\">$email</a>";
#					$url = "<a href=\"$url\">$url</a>";
					foreach(@cline){
						local $l = &change_tg($_);
						$l =~ s/\n/<br>\n/gi;
						print $l , " \n";
					}
				}

				# ルートデータ後処理(変換)
				foreach(@line2){
					local $l = &change_tg($_);
					$l =~ s/\n/<br>/gi;
					print $l , "\n";
				}
			}
		}elsif( m#<!--/main_loop-->#i ){
			next;
		}elsif( m#<!--res_loop-->#i ){
			next;
		}elsif( m#<!--/res_loop-->#i ){
			next;
		}elsif( m#<!--resmode_if-->#i ){
			if( $selno eq "" || $selno <= 0 ){
				while( <IN> ){
					if( m#<!--/resmode_if-->#i ){
						last;
					}
				}
			}
			next;
		}elsif( m#<!--/resmode_if-->#i ){
			next;
		}elsif( m#<!--before_if-->#i ){
			if( $before_no < 0 ){
				while( <IN> ){
					if( m#<!--/before_if-->#i ){
						last;
					}
				}
			}
			next;
		}elsif( m#<!--/before_if-->#i ){
		}elsif( m#<!--next_if-->#i ){
			if( $next_no < 0 ){
				while( <IN> ){
					if( m#<!--/next_if-->#i ){
						last;
					}
				}
			}
			next;
		}elsif( m#<!--/next_if-->#i ){
		}else{
			local $l = &change_tg($_);
			print $l , "\n";
		}
	}
	close( IN );
	return "";
}

# 新規No.取得 ----------------------------------------------------
sub bbs_newno{
	# 追加投稿番号の取得
	local $no = 1;
	open( BBSIN , "$bbs_file" );
	while( <BBSIN> ){
		chop($_);
		if( substr($_,0,7) eq "<start>" ){
			$no = substr($_,7)+1;
			last;
		}
	}
	close(BBSIN);
	return $no;
}

# メッセージ書き込み ------------------------------------------------
sub bbs_write{
	local($no,$parent,$date,$ip,$title,$name,$age,$sex,$email,
	 $url,$fgcol,$bgcol,$delkey,$msg,$etc1,$etc2) = @_;

	# メッセージを管理ファイルに追加
	open( TMPOUT , ">$bbs_tmp" );

	# 投稿番号を出力する
	print TMPOUT "<start>$no\n";

	# 親番号を出力する
	print TMPOUT "<parent>$parent\n";

	# 年月日時分秒を出力する
	print TMPOUT "<date>$date\n";

	# 投稿IPアドレスを出力する
	print TMPOUT "<ip>$ip\n";

	# その他3を出力する
	if( $title ne "" ){
		print TMPOUT "<title>$title\n";
	}

	# 名前を出力する
	if( $name ne "" ){
		print TMPOUT "<name>$name\n";
	}

	# 年齢を出力する
	if( $age ne "" ){
		print TMPOUT "<age>$age\n";
	}

	# 性別を出力する
	if( $sex ne "" ){
		print TMPOUT "<sex>$sex\n";
	}

	# Eメールを出力する
	if( $email ne "" ){
		print TMPOUT "<email>$email\n";
	}

	# URLを出力する
	if( $url ne "http://" ){
		print TMPOUT "<url>$url\n";
	}

	# 文字の色を出力する
	if( $fgcol ne "" ){
		print TMPOUT "<fgcol>$fgcol\n";
	}

	# 背景の色を出力する
	if( $bgcol ne "" ){
		print TMPOUT "<bgcol>$bgcol\n";
	}

	# 削除キーを出力する
	if( $delkey ne "" ){
		print TMPOUT "<delkey>$delkey\n";
	}

	# メッセージを出力する
	if( $msg ne "" ){
		print TMPOUT "<msg>$msg\n</msg>\n";
	}

	# その他1を出力する
	if( $etc1 ne "" ){
		print TMPOUT "<etc1>$etc1\n</etc1>\n";
	}

	# その他2を出力する
	if( $etc2 ne "" ){
		print TMPOUT "<etc2>$etc2\n</etc2>\n";
	}

	# 終了マークを出力する
	print TMPOUT "<end>$no\n";

	open( BBSIN , "$bbs_file" );
	print TMPOUT <BBSIN>;
	close( BBSIN );
	close( TMPOUT );

	# 古いメッセージを管理ファイルから削除
	local $cnt=0;
	open( BBSOUT , ">$bbs_file" );
	open( TMPIN , "$bbs_tmp" );
	while( <TMPIN> ){
		chop($_);
		print BBSOUT "$_\n";
		if( substr($_,0,5) eq "<end>" ){
			$cnt++;
		}
		if( $cnt >= $save_maxmsg ){
			last;
		}
	}
	close( TMPIN );
	close( BBSOUT );
}

# メッセージ削除 ----------------------------------------------------
sub bbs_delete{
	local($no) = @_;
	local $delflg=0;
	local $no_bak;
	local $pno_bak;
	local $initflg=true;

	# メッセージ管理ファイルをテンポラリのコピー
	open( TMPOUT , ">$bbs_tmp" );
	open( BBSIN , "$bbs_file" );
	print TMPOUT <BBSIN>;
	close( BBSIN );
	close( TMPOUT );

	# 指定された投稿番号のメッセージ(子含む)を管理ファイルから削除
	open( BBSOUT , ">$bbs_file" );
	open( TMPIN , "$bbs_tmp" );
	while( <TMPIN> ){
		chop($_);
		if( substr($_,0,7) eq "<start>" ){
			$no_bak = substr($_,7);
			if( $no_bak eq $no ){
				$delflg = 1;
			}
			next;
		}
		if( $delflg == 0 ){
			if( substr($_,0,8) eq "<parent>" ){
				$pno_bak = substr($_,8);
				if( $pno_bak eq $no ){
					$delflg = 1;
					next;
				}else{
					print BBSOUT "<start>$no_bak\n";
				}

				if( $no_bak eq $pno_bak ){
					$initflg=false;
				}
			}
			print BBSOUT "$_\n";
		}
		if( substr($_,0,5) eq "<end>" ){
			$delflg = 0;
		}
	}
	close( TMPIN );
	close( BBSOUT );

	# ルートメッセージNo.が存在しなければ全て削除
	if( $initflg eq true ){
		open( BBSOUT , ">$bbs_file" );
		close( BBSOUT );
	}
}

# ルートメッセージNo.取得 ----------------------------------------
sub bbs_getroot{
	local($p_sno,$dsp_maxmsg) = @_;
	local @rootl_b=();
	local @rootl=();
	local $before=-1;
	local $next=-1;
	local $first_no=0;
	local $cnt_b=0;
	local $cnt=0;
	local $no_bak;
	local $pno_bak;

	if( $p_sno <= 0 ){
		$first_no = 999999999;
	}

	open( BBSIN , "$bbs_file" );
	while( <BBSIN> ){
		chop($_);
		if( substr($_,0,7) eq "<start>" ){
			$no_bak = substr($_,7);
			next;
		}
		if( substr($_,0,8) eq "<parent>" ){
			$pno_bak = substr($_,8);
			if( $no_bak <= $p_sno ){
				$first_no = $pno_bak;
			}
			if( $pno_bak eq $no_bak ){
				if( $first_no < $pno_bak ){
					@rootl_b = (@rootl_b,$pno_bak);
					$cnt_b++;
					next;
				}
				if( $cnt >= $dsp_maxmsg ){
					$next = $pno_bak;
					last;
				}
				@rootl = (@rootl,$pno_bak);
				$cnt++;
			}
		}
	}
	close( BBSIN );
	if( $cnt_b <= 0 ){
		$before = -1;
	}elsif( $cnt_b < $dsp_maxmsg ){
		$before = @rootl_b[0];
	}else{
		$before = @rootl_b[$cnt_b-$dsp_maxmsg];
	}
	@rootl = ($before,$next,@rootl);
	if( $cnt <= 0 && $before >= 0){
		$p_sno = $before;
		@rootl = bbs_getroot($p_sno,$dsp_maxmsg);
	}
	return @rootl;
}

# 返信メッセージNo.取得 ------------------------------------------
sub bbs_getresp{
	local($no) = @_;
	local @clist=();
	local $no_bak;

	open( BBSIN , "$bbs_file" );
	while( <BBSIN> ){
		chop($_);
		if( substr($_,0,7) eq "<start>" ){
			$no_bak = substr($_,7);
		}
		if( substr($_,0,8) eq "<parent>" ){
			$pno_bak = substr($_,8);
			if( $pno_bak eq $no && $no_bak ne $no ){
				@clist = ($no_bak,@clist);
			}
		}
	}
	close( BBSIN );
	return @clist;
}

# メッセージ読み込み ------------------------------------------------
sub bbs_read{
	local($no) = @_;
	local $selflg = 0;
	local($no_bak,$parent,$date,$ip,$title,$name,$age,$sex,$email,
	 $url,$fgcol,$bgcol,$delkey,$msg,$etc1,$etc2);

	open( BBSIN , "$bbs_file" );
	while( <BBSIN> ){
		chop($_);
		if( substr($_,0,7) eq "<start>" ){
			$no_bak = substr($_,7);
			if( $no_bak eq $no ){
				$selflg = 1;
			}
		}
		if( $selflg == 1 ){
			if( substr($_,0,8) eq "<parent>" ){
				$parent = substr($_,8);
			}elsif( substr($_,0,6) eq "<date>" ){
				$date = substr($_,6);
			}elsif( substr($_,0,4) eq "<ip>" ){
				$ip = substr($_,4);
			}elsif( substr($_,0,7) eq "<title>" ){
				$title = substr($_,7);
				$title =~ s/ /&nbsp;/g;
			}elsif( substr($_,0,6) eq "<name>" ){
				$name = substr($_,6);
				$name =~ s/ /&nbsp;/g;
			}elsif( substr($_,0,5) eq "<age>" ){
				$age = substr($_,5);
				$age =~ s/ /&nbsp;/g;
			}elsif( substr($_,0,5) eq "<sex>" ){
				$sex = substr($_,5);
				$sex =~ s/ /&nbsp;/g;
			}elsif( substr($_,0,7) eq "<email>" ){
				$email = substr($_,7);
				$email =~ s/ /&nbsp;/g;
			}elsif( substr($_,0,5) eq "<url>" ){
				$url = substr($_,5);
				$url =~ s/ /&nbsp;/g;
			}elsif( substr($_,0,7) eq "<fgcol>" ){
				$fgcol = substr($_,7);
				$fgcol =~ s/ /&nbsp;/g;
			}elsif( substr($_,0,7) eq "<bgcol>" ){
				$bgcol = substr($_,7);
				$bgcol =~ s/ /&nbsp;/g;
			}elsif( substr($_,0,8) eq "<delkey>" ){
				$delkey = substr($_,8);
				$delkey =~ s/ /&nbsp;/g;
			}elsif( substr($_,0,5) eq "<msg>" ){
				$msg = substr($_,5);
				while( <BBSIN> ){
					chop($_);
					if( substr($_,0,6) eq "</msg>" ){
						last;
					}else{
						$msg .= "\n" . $_;
					}
				}
				$msg =~ s/ /&nbsp;/g;
			}elsif( substr($_,0,6) eq "<etc1>" ){
				$etc1 = substr($_,6);
				while( <BBSIN> ){
					chop($_);
					if( substr($_,0,7) eq "</etc1>" ){
						last;
					}else{
						$etc1 .= "\n" . $_;
					}
				}
				$etc1 =~ s/ /&nbsp;/g;
			}elsif( substr($_,0,6) eq "<etc2>" ){
				$etc2 = substr($_,6);
				while( <BBSIN> ){
					chop($_);
					if( substr($_,0,7) eq "</etc2>" ){
						last;
					}else{
						$etc2 .= "\n" . $_;
					}
				}
				$etc2 =~ s/ /&nbsp;/g;
			}elsif( substr($_,0,5) eq "<end>" ){
				last;
			}
		}
	}
	close( BBSIN );
	return ($no_bak,$parent,$date,$ip,$title,$name,$age,$sex,$email,
	 $url,$fgcol,$bgcol,$delkey,$msg,$etc1,$etc2);
}

# タグ置換 ----------------------------------------------------------
sub change_tg{
	local($l)=@_;


	$l =~ s/<!--bbs_cgi-->/$bbs_cgi/gi;
	$l =~ s/<!--html-->/$html/gi;
	$l =~ s/<!--sid-->/$sid/gi;
	$l =~ s/<!--sname-->/$sname/gi;
	$l =~ s/<!--sno-->/$sno/gi;
	$l =~ s/<!--selno-->/$selno/gi;
	$l =~ s/<!--before_no-->/$before_no/gi;
	$l =~ s/<!--next_no-->/$next_no/gi;

	$l =~ s/<!--no-->/$no/gi;
	$l =~ s/<!--parent-->/$parent/gi;
	$l =~ s/<!--ip-->/$ip/gi;

	local $yyyy=substr($date,0,6);
	$l =~ s/<!--yyyy-->/$yyyy/gi;
	local $yyyymm=substr($date,0,10);
	$l =~ s/<!--yyyymm-->/$yyyymm/gi;
	local $yyyymmdd=substr($date,0,14);
	$l =~ s/<!--yyyymmdd-->/$yyyymmdd/gi;
	local $yyyymmddhh=substr($date,0,18);
	$l =~ s/<!--yyyymmddhh-->/$yyyymmddhh/gi;
	local $yyyymmddhhmm=substr($date,0,22);
	$l =~ s/<!--yyyymmddhhmm-->/$yyyymmddhhmm/gi;
	local $yyyymmddhhmm=substr($date,0,26);
	$l =~ s/<!--yyyymmddhhmmss-->/$yyyymmddhhmm/gi;

	$l =~ s/<!--title-->/$title/gi;
	$l =~ s/<!--name-->/$name/gi;
	$l =~ s/<!--age-->/$age/gi;
	$l =~ s/<!--sex-->/$sex/gi;
	$l =~ s/<!--email-->/$email/gi;

	if( $email ne "" ){
		$l =~ s/<!--emaillink-->/<a href=mailto:$email>$name<\/a>/gi;
	}else{
		$l =~ s/<!--emaillink-->/$name/gi;
	}

	if( $url ne "" ){
		$l =~ s/<!--urllink-->/<a href=$url target="_blank">URL<\/a>/gi;
	}else{
		$l =~ s/<!--urllink-->//gi;
	}

	$l =~ s/<!--url-->/$url/gi;
	$l =~ s/<!--msg-->/$msg/gi;
	$l =~ s/<!--fgcol-->/$fgcol/gi;
	$l =~ s/<!--bgcol-->/$bgcol/gi;
	$l =~ s/<!--delkey-->/$delkey/gi;
	$l =~ s/<!--etc1-->/$etc1/gi;
	$l =~ s/<!--etc2-->/$etc2/gi;
	return $l;
}
sub bbs_trim{
	local($_)=@_;
	$_ =~ s/ *\n/\n/g;
	$_ =~ s/^\n*//g;
	$_ =~ s/[ \n]*$//g;
	return $_;
}
#--------------------------------------------------------------------
1;