#!/usr/bin/perl

# ------------------------------------------------
#
# mimic board model2
# 別窓で返信ができる掲示板スクリプト
#
#
# 30-Apr-2001
# Nobutaka Makino <nobu@pt.imaginet.ne.jp>
#
# ------------------------------------------------

# このソフトウェアは商用目的での使用を除き
# 基本的にライセンスフリーとします。
# 

$Build = '#086';


#================================================
# file path settings
#================================================

$mimic_ini = './mimic2.ini';
$skins_ini = './m2skin.ini';




MAIN: {

	&load_lib($mimic_ini);
	&load_lib($skins_ini);

	&parse_extra_forms if(@Extra);
	&get_cookies;

	# OP窓表示処理
	# --------------------------------------

	my $query = &read_buf;

	   if($query =~ /^reply(\d+)$/         ){ &put_reply_window($1);  }
	elsif($query =~ /^delete(\d+)$/        ){ &put_delete_window($1); }

	# データ操作処理
	# --------------------------------------

	&decode($query);
	&check_required_form;
	&set_default_values;
	my $action = &get_action;
	&put_cookies if($action =~ /^add_/);

	   if($action eq 'add_message'          ){ &add_message('message');   }
	elsif($action eq 'add_reply'            ){ &add_message('reply');     }
	elsif($action eq 'delete_message'       ){ &delete_message;           }
	elsif($action eq 'set_admin_password'   ){ &set_admin_password_step2; } # *

	# それ以外
	# --------------------------------------

	else{
		&put_board;
	}

}

1;
# load_lib.pl
#---------------


# ライブラリ/ファイルをロードする
#-----------------------------------------------------

sub load_lib{

	my $file = shift;

	eval{ require $file; };

	if( $@ ){
		&error("$ERRMSG_LOADLIB_FAILED ($file: $@)");
	}
}

1;
# cookie.pl
#---------------


# 00/08/24 prototpye

# クッキーを取得する
#----------------------------------------------------------

# %C にクッキー値を指定
# $Cookie_name : mimic.ini クッキー名
# $Extra_forms : skins.ini 特別フォーム数

sub get_cookies{


	# 生クッキーを取得
	#-----------------------------

	my $cookies = $ENV{'HTTP_COOKIE'};
	   $cookies =~ s/; /;/g;

	# mimic2クッキーを検索
	#-----------------------------

	my $name  = $Cookie_name;
	my $value;

	for ( split(/;/,$cookies ) ){
		if( /^$name=(.+)$/ ){
			$value = $1;
			$value =~ s/%3B/;/g; # [;]のデコード / [:]はそのまま表示する
			last;
		}
	}


	# 分割 & 代入
	#-----------------------------

	my @values = split(/::/,$value);

	$C{'name'}  = shift @values;
	$C{'email'} = shift @values;
	$C{'url'}   = shift @values || 'http://';

	# extra forms #061
	#-----------------------------


	for(0..$#Extra){
		if($Extra[$_]->{'use_cookie'}){
			@C{'ext'.$_} = shift @values || $Extra[$_]->{'cookie_default_value'};
		}
	}
}




# クッキーを発行する
#----------------------------------------------------------

sub put_cookies{

	if($Use_cookie_checkbox){
		return if !$F{'use_cookies'};
	}
	else{
		return if !$Use_cookies;
	}

	my $name  = $Cookie_name;
	my $limit = &parse_time($Cookie_limit);
	my @values;

	# 値の配列を作成 #061
	#-----------------------------

	push @values, $F{'name'};
	push @values, $F{'email'};
	push @values, $F{'url'};

	for $this(@Extra){
		push @values, $this->{'value'} if($this->{'use_cookie'});
	}


	# 値が無いときクッキーを削除
	#-----------------------------

	$limit = &parse_time("-30d") if( !@values );

	# 時刻の取得
	#-----------------------------

	my($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = gmtime(time + $limit);

	$year = sprintf( "%04d", $year + 1900 );
	$sec  = sprintf( "%02d", $sec  );
	$min  = sprintf( "%02d", $min  );
	$hour = sprintf( "%02d", $hour );
	$mday = sprintf( "%02d", $mday );

	$wday = ('Sun','Mon','Tue','Wed','Thu','Fri','Sat') [$wday];
	$mon  = ('Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec')[$mon];
	my $date = "$wday, $mday\-$mon\-$year $hour:$min:$sec GMT";

	# セパレータ[;][::]をエンコード
	#-----------------------------

	for( @values ){ s/;/%3B/g; s/\::/&#58;&#58;/g; }

	# 値の生成
	#-----------------------------

	my $value =  join('::',@values);

	# 出力
	#-----------------------------

	print "Set-Cookie: $name=$value; expires=$date\n";


	# クッキー値の更新
	#-----------------------------

	for( @values ){ s/%3B/;/g; }

	$C{'name'}  = shift @values;
	$C{'email'} = shift @values;
	$C{'url'}   = shift @values || 'http://';

	#[079]
	for(0..$#Extra){
		$C{'ext'.$_} = shift @values || $Extra[$_]->{'cookie_default_value'};
	}

}

1;
# error.pl
#---------------

sub flush_message{

	return if(!$Set_message_called);

	my $action = &get_action;

	if( $action eq 'add_message' ){
		&put_board;
	}
	else{
		&put_result_window;
	}

}


sub set_message{

	$Error_message .= shift;

	$C{'title'} = $F{'title'};
	$C{'message'} = $F{'message'};
	$C{'message'} =~ s/<br>/\n/ig;

	for(0..$#Extra){
		$C{'ext'.$_} = $Extra[$_]->{'value'};
	}

	$Set_message_called = 1; # flush用フラグ

}

#-------

sub error{


	print "Content-type: text/html\n\n";
	#---------------------------------------------------------------------
	print qq(

<html>
<style><!--
	td {
		font-size: 12px;
		font-family: "helvetica", "arial",;
	}
	input{
		border-top:    1px solid;
		border-right:  1px solid;
		border-bottom: 1px solid;
		border-left:   1px solid;
	}
--></style>
<title>エラーが発生しました</title>
<body bgcolor="#FFFFFF" text="#000000" link="#003399" vlink="#003399" alink="#003399">

<div align="center">
<table border="0" cellspacing="0" cellpadding="0" height="100%"><tr><td> 
<table border="0" cellpadding="20" cellspacing="0" width="350" height="250">
	<tr><td nowrap height="20"> <font size="3">エラーが発生しました</font><br>
		An error has occured<br>
		<hr size="1">
	</td></tr>
	<tr><td>
		<div align="center">
			@_
		</div>
	</td></tr>
	<tr><td nowrap height="20"> 
		<hr size="1">
		<div align="right"> <font font><i> <a href="http://www.imaginet.ne.jp/%7Enobu/"> 
			Mimic Board the 2nd</a> $Build<br>
			(c) 1999-2000 Nobutaka Makino</i></font></div>
</td></tr></table>
</td></tr></table>
</center>
</html>

);

	#---------------------------------------------------------------------

	exit;

}

1;
# misc.pl
#---------------

# 文字を短くする(２バイト文字対応)
#-----------------------------------------------------

# update 2001.04.30 [#080]
# うまく切れてなかったのを修正

sub to_short_string{

	my $string = shift;
	my $size   = shift;

	my $length = length($string);

	# 短くしなくて良いなら終わり
	#-----------------------------

	return $string if !$size;
	return $string if  $size > $length;

	&load_lib( $Jcode );

	my $count;
	my $new_string;
	my $is2byte;

	while( $count!=$length && $count<$size ){

		my $char = substr($string,$count,2);		# 2文字取得してみる
		($is2byte,undef) = &jcode::getcode(*char);	# 漢字かな？

		if( $is2byte){
			$new_string .= $char; $count+=2;
		}
		else{
			$new_string .= substr($string,$count++,1);
		}

	}

	return $new_string . '...';

}


# 書き込み時刻の取得
#-----------------------------------------------------

sub get_date{


	my $date = $Date_format;
	my $diff = &parse_time($Time_difference);

	#---------------------
	# 書き込み時間の取得

	my $week;
	my($sec,$min,$hour,$mday,$mon,$year,$wday) = localtime(time + $diff );

	my $hour_t = $hour;
	if($hour_t > 12){ $hour_t -= 12 };
	if($hour_t == 0){ $hour_t += 12 }; # [#081]

	my $tt;
	if($hour < 12 ){ $tt = $Am_tt; } #086
	else           { $tt = $Pm_tt; } #

	for($date){

		s/MMM/<!A>/;
		s/ddd/<!B>/;
		s/tt/<!C>/;

		s/yyyy/ sprintf("%02d", $year+1900)/e;
		s/yy/   sprintf("%02d", $year%100 )/e;
		s/MM/   sprintf("%02d", $mon+1)    /e;
		s/M/    $mon+1                     /e;
		s/dd/   sprintf("%02d", $mday)     /e;
		s/d/    $mday                      /e;
		s/HH/   sprintf("%02d", $hour)     /e;
		s/H/    $hour                      /e;
		s/hh/   sprintf("%02d", $hour_t)   /e;
		s/h/    $hour_t                    /e;
		s/mm/   sprintf("%02d", $min)      /e;
		s/m/    $min                       /e;
		s/ss/   sprintf("%02d", $sec)      /e;
	    s/s/    $sec                       /e;

		s/<!A>/  $Mons[$mon]               /e;#
		s/<!B>/  $Days[$wday]              /e;#
		s/<!C>/$tt/;

	}

	return $date;

}

# リモートホスト名の取得
#-----------------------------------------------------

sub get_host{

	my $host = $ENV{'REMOTE_HOST'};
	my $addr = $ENV{'REMOTE_ADDR'};

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

	return $host || $addr;

}



1;
# lock.pl
#---------------

# ファイルロック
#----------------------------------------------------------
# スカラ値でもファイルハンドル扱いされる

sub lock{

	return if(!$Use_lock);

	my $handle = shift;

	eval{ flock($handle,2); };

	if($@){
		&error("$ERRMSG_FLOCK_FAILED ($handle: $@) ");
	}

}

1;
# decode.pl
#---------------

# デコード
#----------------------------------------------------------

sub decode{


	my $tags    = $Arrow_tags;
	my $link    = $Auto_link;

	my $buffer = shift;

	&load_lib($Jcode);
	&load_lib($Ntc) if($tags || $link );

	my @pairs = split(/&/,$buffer);

	   my($name );
	local($value,$count);

	foreach(@pairs){

		($name, $value) = split(/=/);

		 $value =~ tr/+/ /;
		 $value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg;

		 $value =~ s/\r\r/\n/go;
		 $value =~ s/\r\n/\n/go;
		 $value =~ s/\n/\t/go;

		# 文字コード変換
		#--------------------------

		 &jcode'h2z_sjis(*value);
		 &jcode'convert(*value,'sjis');

		 $value =~ s/\,/&#44;/g;

		# タグの処理 / 後で見直し
		#--------------------------

		if( $tags ){
			&ntc'tag(*value,$tags, 0,     0) if( $name ne 'message' ); #リンクしない
			&ntc'tag(*value,$tags, 0, $link) if( $name eq 'message' ); #リンクする
		}
		else{
			$value =~ s/\</&lt;/go;
			$value =~ s/\>/&gt;/go;
			&ntc'tag(*value,$tags, 0,$link) if( $name eq 'message' && $link );
		}

		$value  =~ s/\t/<br>/go;

        # spamを排除
        $count = 0;
        while($value =~ /href/g) {
                $count++;
        }
        if($count > 3) {
                $value = '';
        }
		

		# ハッシュ代入
		#--------------------------

		$Extra[$1]->{'value'} = $value if($name =~ /^ext(\d+)$/);
		$F{$name}             = $value;
	}


	# 'addr'
	if($F{'addr'}){
		if($F{'addr'} =~ m|\@|      ){ $F{'email'} = $F{'addr'}; }
		if($F{'addr'} =~ m|^http://|){ $F{'url'}   = $F{'addr'}; }
	}


	# 簡易フォーマットチェック
	$F{'url'}   = '' if( $F{'url'}   !~ m|^http://(.+)\.(.+)$| );
	$F{'email'} = '' if( $F{'email'} !~ m|^(.+)@(.+)\.(.+)$|   );

	# 061 Extra入力値チェック
	if( &get_action =~ /^add_/ ){
		for $this(@Extra){

			next if(!$this->{'value'}); # 後でrequiredチェック
			next if(!$this->{'use_type_check'});

			if($this->{'value'} !~ /$this->{'type_check_regexp'}/){

				if($this->{'type_check_error_mode'}){
					&set_message( $this->{'type_check_error_message'} );
					&flush_message;
				}
				else{
					$this->{'value'} = '';
				}
			}

		}
	}




}

1;
# get_action.pl
#---------------

# 00/08/26 うーん。存在意義があるのか。

# 00/08/24 アクション名を抽出する
# 基本的にactionは単項で渡される

# Extract Action name.
#------------------------------------------------


sub get_action{

	return $F{'action'} if($F{'action'});

	for(@Actions){
		return $_ if( exists $F{$_} || exists $F{"$_.x"} || exists $F{"$_.y"} );
	}

}

1;
# put_board.pl
#---------------



sub put_board{

	local @Data = &load_data($Data_file); # for extract reply data!
	my $head = shift @Data;
	&check_header($head);

	print "Expires: 01/01/70 00:00:00 GMT\n";
	print "Content-type: text/html\n\n";

	&page_init;
	&format_init;

	&replace_print($Format_head);

	my $count;
	while($_ = shift @Data){

		# adjust display arguments
		#------------------------------
		next if(/^\t/); #

		$count++;

		if($count < $Range_start){ $Put_page_ctrl_prev = 1; next; }
		if($count > $Range_last ){ $Put_page_ctrl_next = 1; last; }

		# extract data
		#------------------------------

		s/\n$//; # chomp;

		my $fmt;

		my($mid,$name,$email,$url,$title,$message,$date,$host,$time,$password,@extra) = split (/,/,$_);
# E-Mail偽装
if ($email) {
	$email =~ s/\@/\%/g;
	$email =~ s/\./\@/g;
}

		if($Link_main_url_on_name   && $url   ){ $name = qq|<a $Link_option href="$url">$name</a>|;          }
		if($Link_main_email_on_name && $email ){ $name = qq|<SCRIPT type='text/javascript'>\n<!--\nmail(\"$email\");\n//-->\n</SCRIPT>$name</a>|; }



		# anchor
		#------------------------------

		if($url){ ( $fmt = $Format_main_url ) =~ s/\$url/$url/g; $url = $fmt; }
		else{       $url = $Format_main_url_blank                             }

		if($email){ ( $fmt   = $Format_main_email ) =~ s/\$email/$email/g; $email = $fmt; }
		else{         $email = $Format_main_email_blank;                                  }

		for(@Extra_subscripts_which_use_formats){
			if($extra[$_]){
				( $fmt       = $Extra[$_]->{'replace_format_main'} ) =~ s/\$value/$extra[$_]/g;
				  $extra[$_] = $fmt;
			}
			else{
				  $extra[$_] = $Extra[$_]->{'replace_format_main_blank'};
			}
		}



		my $reply_data = &extract_reply_data;

		# replace ! 
		#------------------------------

		for($temp=$Format_data){

			s/\$Cgi_url/$Cgi_url/sg;
			s/\$mid/$mid/sg;
			s/\$name/$name/sg;
			s/\$email/$email/sg;
			s/\$url/$url/sg;
			s/\$title/$title/sg;
			s/\$message/$message/sg;
			s/\$date/$date/sg;
			s/\$host/$host/sg;
			s/\$time/$time/sg;
			s/\$ext(\d+)/$extra[$1]/sg;

			s/\$loop_value/$Loop_values[ $mid % ( $#Loop_values + 1) ]/g if($#Loop_values >= 0 );
			s/\$random_value/$Random_values[ int( rand($#Random_values+1) ) ]/eg;

			s/\$reply_data/$reply_data/sg;

			print;
		}
	}

	&page_control_init;
	&replace_print($Format_last) if(!$Put_page_ctrl_next);
	&replace_print($Format_foot);

	exit;

}

#---------------------------------------------------------------







1;
# replace_print.pl
#---------------

sub replace_print{

	my $line = shift;

	for($line){

		# base
		s/\$cgi_url/$Cgi_url/sg;
		s/\$home_url/$Home_url/sg;

		# page control
		s/\$prev/$Prev/sg;
		s/\$next/$Next/sg;

		# cookeis or default values
		s/\$cook_name/$C{'name'}/sg;
		s/\$cook_email/$C{'email'}/sg;
		s/\$cook_url/$C{'url'}/sg;
		s/\$cook_addr/$C{'email'}||$C{'url'}/esg;
		s/\$cook_title/$C{'title'}/sg;
		s/\$cook_message/$C{'message'}/sg;

		s/\$cook_ext(\d+)/$C{'ext'.$1}/sg;

		# others
		s/\$build/$Build/sg;
		s/\$error_message/$Error_message/sg;

		s/\$op_window_height/$Op_window_height/sg;
		s/\$op_window_width/$Op_window_width/sg;

		print;

	}
}

1;
# put_board_etc.pl
#---------------

# ページ設定を行う
#----------------------------------------------------------

sub page_init{

	my $current = $F{'page'} || 1;

	$Page_next = $current+1;
	$Page_prev = $current-1;

	$Range_last  = $current * $Arguments;
	$Range_start = $Range_last - $Arguments +1;

}

#
#----------------------------------------------------------

sub page_control_init{

	if($Put_page_ctrl_prev){ ( $Prev = $Format_page_ctrl_prev ) =~ s/\$url/$Cgi_url?page=$Page_prev/g; }
	else{                      $Prev = $Format_page_ctrl_prev_off;                                     }

	if($Put_page_ctrl_next){ ( $Next = $Format_page_ctrl_next ) =~ s/\$url/$Cgi_url?page=$Page_next/g; }
	else{                      $Next = $Format_page_ctrl_next_off;                                     }

}




# ファイルをスカラ値に読み込む
#----------------------------------------------------------

sub load_data{

	my $file = shift;

	open(FH, $file) || &error("$ERRMSG_OPEN_FAILED ($file: $!)");
		&lock(FH);
		my @line = <FH>;
	close(FH);

	return @line;

}


1;
# put_board_reply.pl
#---------------

# extract reply data

sub extract_reply_data{

	my @reply;
	my $buffer;

	while($Data[0] =~ /^\t/){
		my $data = shift @Data;
		   $data =~ s/^\t//;
		   $data =~ s/\n$//;
		push @reply, $data;
	}

	for(@reply){

		# split data
		#------------------------------

		($mid,$name,$email,$url,$title,$message,$date,$host,$time,$password,@extra) = split (/,/,$_);

		# set anchors
		#------------------------------

		my $fmt;
if ($email) {
	$email =~ s/\@/\%/g;
	$email =~ s/\./\@/g;
}

		if($Link_reply_url_on_name && $url     ){ $name = qq|<a $Link_option href="$url">$name</a>|;          }
		if($Link_reply_email_on_name && $email ){ $name = qq|<SCRIPT type='text/javascript'>\n<!--\nmail(\"$email\");\n//-->\n</SCRIPT>$name</a>|; }

		if($url){ ( $fmt = $Format_reply_url ) =~ s/\$url/$url/g; $url = $fmt; }
		else{       $url = $Format_reply_url_blank                             }

		if($email){ ( $fmt   = $Format_reply_email ) =~ s/\$email/$email/g; $email = $fmt; }
		else{         $email = $Format_reply_email_blank;                                  }

		for(@Extra_subscripts_which_use_formats){
			if($extra[$_]){
				( $fmt       = $Extra[$_]->{'replace_format_reply'} ) =~ s/\$value/$extra[$_]/g;
				  $extra[$_] = $fmt;
			}
			else{
				  $extra[$_] = $Extra[$_]->{'replace_format_reply_blank'};
			}
		}


		# replace ! 
		#------------------------------


		for($temp=$Format_reply){

			s/\$Cgi_url/$Cgi_url/g;

			s/\$mid/$mid/sg;
			s/\$name/$name/sg;
			s/\$email/$email/sg;
			s/\$url/$url/sg;
			s/\$title/$title/sg;
			s/\$message/$message/sg;
			s/\$date/$date/sg;
			s/\$host/$host/sg;
			s/\$time/$time/sg;
			s/\$ext(\d+)/$extra[$1]/sg;

			s/\$loop_value/$Loop_values[ $mid % ( $#Loop_values + 1) ]/g if($#Loop_values >= 0 );
			s/\$random_value/$Random_values[ int( rand($#Random_values+1) ) ]/eg;

			$buffer .= $_;
		}
	}

	return $buffer;

}



1;

# read_buf.pl
#---------------


# フォーム値取得
#----------------------------------------------------------

sub read_buf{

	my $buffer;

	read(STDIN, $buffer, $ENV{'CONTENT_LENGTH'} );
	$buffer = $ENV{'QUERY_STRING'} if( !$buffer );

	return $buffer;

}

1;
# format_init.pl
#---------------



# フォーマットを抽出
#----------------------------------------------------------

sub format_init{

	my $format = &load_format($Format_mimic_main);

	for($format){

		if(/<!--m2head_begin-->(.*)<!--m2head_end-->/s){ $Format_head =$1; }
		if(/<!--m2data_begin-->(.*)<!--m2data_end-->/s){ $Format_data =$1; }
		if(/<!--m2last_begin-->(.*)<!--m2last_end-->/s){ $Format_last =$1; }
		if(/<!--m2foot_begin-->(.*)<!--m2foot_end-->/s){ $Format_foot =$1; }

		for($Format_data){
			if( s/<!--m2data_reply_begin-->(.+)<!--m2data_reply_end-->/\$reply_data/s ){ $Format_reply = $1; }
		}
	}

}

# ファイルをスカラ値に読み込む
#----------------------------------------------------------

sub load_format{

	my $file = shift;

	open(FMT, $file) || &error("$ERRMSG_OPEN_FAILED ($file: $!)");
		$line = join('',<FMT>);
	close(FMT);

	return $line;

}


1;
# set_admin_password.pl
#---------------

# 管理者パスワード登録
#----------------------------------------------------------

sub set_admin_password_step2{


	# encode the password
	#--------------------------------------------

	my $encoded_password = &encode_password($F{'password'});

	# open the file
	#--------------------------------------------

	open(FH, "+<$Data_file") || &error("$ERRMSG_OPEN_FAILED ($file: $!)");

		&lock(FH);
		my @data = <FH>;
		my $head = shift @data;

		# already password exists
		#----------------------------------------
		if($head =~ /^mimic board2:(.+):(.*)$/){
			close FH;
			&error("$ERRMSG_ADMIN_PASSWORD_NOT_BLANK");
		}

		# extract last_mid from header of mimic board1
		#----------------------------------------
		if($head =~ /[^,]+,(\d+),\d+,[^,]+$/){
			$last_mid = $1;
			&set_message($MSG_HEADER_CONVERTED);
		}

		# add header
		#----------------------------------------
		unshift(@data, "mimic board2:$encoded_password:$last_mid\n");


	# write on the file
	#--------------------------------------------

	seek(FH,0,0);
	truncate(FH,0);
	print FH @data;
	close FH;

	&set_message($MSG_ADMIN_PASSWORD_SET_SUCCEED);
	&put_board;

}



# 管理者パスワード登録画面出力
#----------------------------------------------------------

sub set_admin_password_step1{

	print "Expires: 01/01/70 00:00:00 GMT\n";
	print "Content-type: text/html\n\n";

	#---------------------------------------------------------------------
	print qq(
<html>
<style><!--
	td {
		font-size: 12px;
		font-family: "helvetica", "arial",;
	}
	input{
		border-top:    1px solid;
		border-right:  1px solid;
		border-bottom: 1px solid;
		border-left:   1px solid;
	}
--></style>
<title>管理者パスワードのセットアップ</title>
<body bgcolor="#FFFFFF" text="#000000" link="#003399" vlink="#003399" alink="#003399">

<div align="center">
<table border="0" cellspacing="0" cellpadding="0" height="100%"><tr><td> 
<table border="0" cellpadding="20" cellspacing="0" width="350" height="250">
<form method="post" action="$Cgi_url">
<input type="hidden" name="action" value="set_admin_password">
	<tr><td nowrap height="20"> <font size="3">管理者パスワードの設定</font><br>
		Admin password setup<br>
		<hr size="1">
	</td></tr>
	<tr><td nowrap> 
		<div align="center"> new password: 
		<input type="password" name="password">
		<input type="submit" value="submit">
		</div>
	</td></tr>
	<tr><td nowrap height="20"> 
		<hr size="1">
		<div align="right"> <font font><i> <a href="http://www.imaginet.ne.jp/%7Enobu/"> 
			Mimic Board the 2nd</a> $Build<br>
			(c) 1999-2000 Nobutaka Makino</i></font></div>
	</td></tr>
</form>
</table>
</td></tr></table>
</center>
</html>

);

	#---------------------------------------------------------------------


}


1;
# password.pl
#---------------


# salt取得 & 暗号化
#----------------------------------------------------------


sub encode_password{

	my $raw_password = shift;

	return '' if(!$raw_password);

	my $salts = q|./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz|;

	srand(time/$$); # umm..- #

	my $salt  = substr($salts, int(rand(63)),1);
	   $salt .= substr($salts, int(rand(63)),1);

	return crypt($raw_password,$salt);
}

# パスワード照合
#----------------------------------------------------------

sub password_verify{

	my $given_password   = shift;
	my $encoded_password = shift;

	return 0
		if(!$given_password || !$encoded_password);

	return
		  ( $encoded_password eq crypt($given_password,$encoded_password) );

}

1;
# prase_time.pl
#---------------


sub parse_time{

	my $string = shift;
	my $parsed = $string;

	for($parsed){

		s/s//g;
		s/m/*60/g;
		s/h/*60*60/g;
		s/d/*60*60*24/g;
		s/M/*60*60*24*30/g;
		s/y/*60*60*24*365/g;

	}

	my $evaled = eval $parsed;

	if(!$@ && ($evaled =~ /^\d*$/ )){ return $evaled;     }
	else{ &error("$ERRMSG_PARSE_TIME_FAILED ( $string -> $parsed )"); }

}

1;
# add_message.pl
#---------------


sub add_message{

	# check action
	#----------------------------------

	my $add_message;
	my $add_reply;

	if( shift eq 'message'){ $add_message = 1; }
	else                   { $add_reply = 1;   }


	# adding reply, disabled ?
	#-----------------------------------
	if($add_reply && $Disable_adding_reply ){
		&set_message( $MSG_ADDING_REPLY_DISABLED );
		&put_result_window;
	}


	# file open
	#------------------------------------------------------

	open(FH, "+<$Data_file") || &error("$ERRMSG_OPEN_FAILED ($file: $!)");

		&lock(FH);
		my @data = <FH>;

		my $head = shift @data;
		my($encoded_admin_password,$last_mid) = &check_header($head);

		$last_mid++;

		# require admin password when you add message.
		#--------------------------------------------------

		my $set_message;

		if(	( $add_message && $Admin_password_required_when_add_message && !&password_verify($F{'password'},$encoded_admin_password)) ||
			( $add_reply   && $Admin_password_required_when_add_reply   && !&password_verify($F{'password'},$encoded_admin_password)) ){

			&set_message( $MSG_WRONG_ADMIN_PASSWORD );
		}


		# check double post
		#--------------------------------------------------

		# seek the latest data

		my $target_mid = $F{'target_mid'};
		my $target_data;

		if($add_message){
			$target_data = $data[0];
		}

		# 重複チェックアルゴリズム変更 (084)
		# target_mid : 返信対象のメッセージID
		# 返信対象が含まれるツリーの一番最後のメッセージを取り出して照合

		else{
			my $found = 0;
			my $prev;
			for(@data){
				if($found && !/^\t/   ){ last;                 } # 親記事が来たら、その１個前を取り出したい
				if($found             ){ $prev = $_;           } # 一個前をセット
				if(/(^|\t)$target_mid/){ $found=1; $prev = $_; } # ターゲットが見つかった！
			}
			$target_data = $prev;
			if(!$target_data){ &set_message("$MSG_DATA_NOT_FOUND ( message id: $target_mid )"); }
		}


		# split the latest data
		(undef,undef,undef,undef,undef,$last_message,undef,$last_host) = split(/,/,$target_data);


		if( $Check_double_post && ($last_message eq $F{'message'}) && ($last_host eq &get_host)){
			 &set_message( $MSG_DOUBLE_POST );
		}


		# if any warning, print it.
		#--------------------------------------------------

		if( $Set_message_called ){
			close FH;
			&flush_message;
		}


		# extract extraform values
		#--------------------------------------------------

		my @extra;
		for $this(@Extra){ push @extra, $this->{'value'}; }


		# create the new message
		#--------------------------------------------------

		my $new_data = join(',',
			$last_mid, $F{'name'}, $F{'email'}, $F{'url'}, $F{'title'}, $F{'message'},
			&get_date, &get_host, time, &encode_password($F{'password'}), @extra );


		my $new_head = "mimic board2:$encoded_admin_password:$last_mid";


		# adjust number of message, and add the message.
		#--------------------------------------------------

		if($add_message){

			my $count;

			for(@data){
				$count++ if(!/^\t/);
				$_ = ''  if($count >= $Arguments_max);
			}

			unshift(@data, $new_data."\n" );
		}


		# or add the reply to the place
		#--------------------------------------------------

		else{

			my $reply_limit = $Limit_number_of_reply_messages;
			my $move_above  = $Move_the_tree_above_when_replied;

			my $target_found;
			my $number_of_tree;
			my $parent_index;

			for(0..$#data+1){ # 最後のデータも消せるように

				if( $data[$_] !~ /^\t/){

					# if found ...
					#--------------------------------------
					if($target_found){

						# check the number of replies.
						#----------------------------------
						if($reply_limit && $number_of_tree > $reply_limit){
							&set_message("$MSG_TOO_MANY_REPLIES ( message id: $target_mid )");
							close FH; &put_result_window; # :exit:
						}

						# add type1 move above.
						#----------------------------------
						if($Move_the_tree_above_when_replied ){
							@trees = splice(@data,$parent_index,$number_of_tree);
							unshift(@data,"\t$new_data\n");
							unshift(@data, @trees );
						}

						# add type2 stay there
						#----------------------------------
						else{
							splice(@data,$_,0,"\t$new_data\n");
						}

						last;
					}

					# initialize the tree info.
					#--------------------------------------
					$number_of_tree = 0;
					$parent_index = $_;
				}

				# set a found flag
				#------------------------------------------
				if( $data[$_] =~ /(^|\t)$target_mid,/){
					$target_found = 1;
				}

				$number_of_tree++;
			}

			if($target_found){ &set_message("$MSG_ADD_REPLY_SUCCEEDED ( message id: $last_mid )"); }
			else             { &set_message("$MSG_ADD_REPLY_FAILED ( message id: $last_mid )");    }

		}

		unshift(@data, $new_head."\n" );

	seek(FH,0,0);
	truncate(FH,0);
	print FH @data;
	close FH;

	if( $add_message ){ &put_board; }
	else              { &put_result_window; }

}

1;
# check_header.pl
#---------------


sub check_header{

	my $head = shift;

	if($head !~ /^mimic board2:(.+)$/){
		&set_admin_password_step1;
		exit;
	}
	else{
		return split(/:/,$1);
	}

}

1;
# parse_extra_forms.pl
#---------------



sub parse_extra_forms{

# エラー検出など？

	for(0..$#Extra){

		# 表示ループの処理時間短縮対策
		if($Extra[$_]->{'use_replace_format'} ){
			push @Extra_subscripts_which_use_formats, $_;
		}


		# require setting check
		if($Extra[$_]->{'required'} && !$Extra[$_]->{'required_message'} ){
			error($ERRMSG_REQUIRED_MESSAGE_REQUIRED);
		}

		if($Extra[$_]->{'use_type_check'} && !$Extra[$_]->{'type_check_error_message'} ){
			error($ERRMSG_TYPE_CHECK_MESSAGE_REQUIRED);
		}

	}

}

1;

# check_required_form.pl
#---------------


sub check_required_form{


	my $action = &get_action;

	# write a message
	#------------------------------------------------------

	if($action =~ /^add_(message|reply)/){

		if($1 eq 'message'){ $add_message = 1; }
		else               { $add_reply = 1;   }

		# require admin password when you add message.
		#--------------------------------------------------
		if( $add_message && $Admin_password_required_when_add_message && !$F{'password'} ){
			 &set_message( $MSG_ADMIN_PASSWORD_REQUIRED  );
		}

		# require admin password when you add message.
		#--------------------------------------------------
		if( $add_reply && $Admin_password_required_when_add_reply && !$F{'password'} ){
			 &set_message( $MSG_ADMIN_PASSWORD_REQUIRED  );
		}

		# check required forms
		#--------------------------------------------------

		if( $Require_name     && !$F{'name'}     ){ &set_message( $MSG_NAME_REQUIRED  ); }
		if( $Require_email    && !$F{'email'}    ){ &set_message( $MSG_EMAIL_REQUIRED ); }
		if( $Require_url      && !$F{'url'}      ){ &set_message( $MSG_URL_REQUIRED   ); }
		if( $Require_title    && !$F{'title'}    ){ &set_message( $MSG_TITLE_REQUIRED ); }
		if( $Require_message  && !$F{'message'}  ){ &set_message( $MSG_MESSAGE_REQUIRED  );        }
		if( $Require_password && !$F{'password'} ){ &set_message( $MSG_PASSWORD_REQUIRED );        }
		if( $add_reply     && !$F{'target_mid'}  ){ &set_message( $MSG_TARGET_MID_REQUIRED_REPLY ); }

		# make extra data.
		#--------------------------------------------------
	
		for $this(@Extra){
			if( $this->{'required'} && !($this->{'value'}) ){
				&set_message($this->{'required_message'});
			}
		}

		# if any warning, push it.
		#--------------------------------------------------

		&flush_message;

	}

	# delete a message
	#------------------------------------------------------

	elsif($action eq 'delete_message'){

		# check required forms
		#--------------------------------------------------

		if(!$F{'target_mid'}){ &set_message($MSG_TARGET_MID_REQUIRED_DELETE);}
		if(!$F{'password'}  ){ &set_message($MSG_PASSWORD_REQUIRED);         }

		# error and exit;
		#--------------------------------------------------

		&flush_message;

	}

	# set admin password
	#------------------------------------------------------

	elsif($action eq 'set_admin_password'){

		# check required forms
		#--------------------------------------------------

		if(!$F{'password'}){
			&error($ERRMSG_ADMIN_PASSWORD_REQUIRED);
		}

	}

}
1;
# put_op_result_window.pl
#---------------

# $error_messageを置換するだけの関数？

sub put_result_window{

	my $format = &load_format($Format_op_result);

	print "Expires: 01/01/70 00:00:00 GMT\n";
	print "Content-type: text/html\n\n";

	&replace_print($format);

	exit;
}

1;
# put_op_delete_window.pl
#---------------

# ターゲットMIDの記事の情報をロードする

sub put_delete_window{

	my $target_mid = shift;
	my @data       = &load_data($Data_file);
	my $format     = &load_format($Format_op_delete);

	for(@data){
		if(/(^|\t)$target_mid,/){
			($target_data=$_) =~ s/^\t//;
			last;
		}
	}

	if(!$target_data){ &set_message("$MSG_DATA_NOT_FOUND (message id: $target_mid)"); &put_result_window; }
	my ($mid,$name,$email,$url,$title,$message,$date,$host,$time,$password,@extra) = split (/,/,$target_data);

	print "Expires: 01/01/70 00:00:00 GMT\n";
	print "Content-type: text/html\n\n";

	for($format){

		# base
		s/\$cgi_url/$Cgi_url/sg;
		s/\$home_url/$Home_url/sg;

		# others
		s/\$build/$Build/sg;
		s/\$mid/$mid/sg;

		# 削除記事確認用？
		# from: put_board
		s/\$name/$name/sg;
		s/\$email/$email/sg;
		s/\$url/$url/sg;
		s/\$title/$title/sg;
		s/\$message/$message/sg;
		s/\$date/$date/sg;
		s/\$host/$host/sg;
		s/\$time/$time/sg;
		s/\$ext(\d+)/$extra[$1]/sg;

		print;

	}

	exit;
}

1;
# put_op_reply_window.pl
#---------------

# ターゲットMIDの記事の情報をロードする

sub put_reply_window{

	my $target_mid = shift;
	my @data       = &load_data($Data_file);
	my $format     = &load_format($Format_op_reply);

	for(@data){
		if(/(^|\t)$target_mid,/){
			($target_data = $_) =~ s/^\t//;
			last;
		}
	}

	if(!$target_data){ &set_message($MSG_DATA_NOT_FOUND); &put_result_window; }
	my ($mid,$name,$email,$url,$title,$message,$date,$host,$time,$password,@extra) = split (/,/,$target_data);

	my $reply_messae;
	my $reply_title = $Reply_title_head . &to_short_string($title,$Reply_title_lengh_limit);
	($reply_message = $message) =~ s/(^|<br>)/\n$Reply_message_head/g;

	print "Expires: 01/01/70 00:00:00 GMT\n";
	print "Content-type: text/html\n\n";

	for($format){

		# base
		s/\$cgi_url/$Cgi_url/sg;
		s/\$home_url/$Home_url/sg;

		# others
		s/\$build/$Build/sg;
		s/\$mid/$mid/sg;

		# from: replace_print
		s/\$cook_name/$C{'name'}/sg;
		s/\$cook_email/$C{'email'}/sg;
		s/\$cook_url/$C{'url'}/sg;
		s/\$cook_addr/$C{'email'}||$C{'url'}/esg;
		s/\$cook_title/$C{'title'}/sg;
		s/\$cook_message/$C{'message'}/sg;

		s/\$cook_ext(\d+)/$C{'ext'.$1}/sg; 

		# from: put_board
		s/\$mid/$mid/sg;
		s/\$name/$name/sg;
		s/\$email/$email/sg;
		s/\$url/$url/sg;
		s/\$title/$title/sg;
		s/\$message/$message/sg;
		s/\$date/$date/sg;
		s/\$host/$host/sg;
		s/\$time/$time/sg;
		s/\$ext(\d+)/$extra[$1]/sg;

		# original 
		s/\$reply_title/$reply_title/sg;
		s/\$reply_message/$reply_message/sg;

		print;

	}

	exit;

}

1;
# delete_message.pl
#---------------

sub delete_message{

	# 管理者のみ削除可能
	# 削除禁止
	# 投稿者パスワード!?


	   my $target_mid = $F{'target_mid'};
	   my $last_mid;

	local $encoded_admin_password;
	local $given_password = $F{'password'};


	open(FH, "+<$Data_file") || &error("$ERRMSG_OPEN_FAILED ($file: $!)");

		&lock(FH);
		my @data = <FH>;

		# admin password check 
		#------------------------------


		my $head = shift @data;
		  ($encoded_admin_password,$last_mid) = &check_header($head);

		# delete message
		#------------------------------

		my $delete_tree = $Delete_the_tree_when_parent_deleted;
		my $parent_index;
		my $number_of_tree;
		my $target_found;

		if($delete_tree){

			for(0..$#data+1){
				if($data[$_] !~ /^\t/){
					if($target_found){ splice(@data,$parent_index,$number_of_tree); last; }
					$parent_index = $_;
					$number_of_tree=0;
				}

				if($data[$_] =~ /(^|\t)$target_mid,/){
					&verify_delete_password($data[$_]);
					$target_found = 1;
					if($1 eq "\t"){ splice(@data,$_,1); last; }
				}

				$number_of_tree++;
			}
		}

		else{
			# not deltree
			for(0..$#data){
				if($data[$_] =~ /(^|\t)$target_mid,/){
					&verify_delete_password($data[$_]);
					$data[$_+1] =~ s/^\t// if($1 ne "\t");
					splice(@data,$_,1);
					$target_found = 1; last;
				}
			}
		}

		if($target_found){ &set_message("$MSG_DELETE_MESSAGE_SUCCEEDED ( message id: $target_mid )"); }
		else             { &set_message("$MSG_DELETE_MESSAGE_FAILED ( message id: $target_mid )");    }


	unshift(@data, $head );

	seek(FH,0,0);
	truncate(FH,0);
	print FH @data;
	close FH;

	&put_result_window;

}



sub verify_delete_password{

	my $admin_only  = $Delete_with_admin_password_only;
	my $target_data = shift;
	   $target_data =~ s/\n$//;

	my $encoded_user_password = (split(/,/,$target_data) )[9];

	my $admin_result = &password_verify($given_password, $encoded_admin_password );
	my $user_result =  &password_verify($given_password, $encoded_user_password );


	my $result;

	if($admin_only){
		$result = $admin_result;
	}
	else{
		$result = $admin_result || $user_result;
	}

	if(!$result){
		close FH;
		&set_message($MSG_WRONG_PASSWORD);
		&put_result_window;
	}

	return;

}



1;
# set_default_values.pl
#---------------


sub set_default_values{

	# set_default value
	#--------------------------------------------------

	$F{'name'}    ||= $Default_name;
	$F{'title'}   ||= $Default_title;
	$F{'message'} ||= $Default_message;

	for $this(@Extra){

		next if($this->{'value'});

		if($this->{'use_default_random_value'}){
			my $uniq_subscripts = int( rand( $#{ $this->{'default_random_values'} } + 1 ));
			$this->{'value'} = $this->{'default_random_values'}[ $uniq_subscripts ];
		}

		$this->{'value'} ||= $this->{'default_value'};

	}

}

1;
