#!/usr/bin/perl

#┌─────────────────────────────────
#│ SMART BLOG : index.cgi - 2022/07/16
#│ copyright (c) kentweb, 1997-2022
#│ https://www.kent-web.com/
#└─────────────────────────────────

# モジュール宣言
use strict;
use CGI::Carp qw(fatalsToBrowser);
use lib "./lib";
use CGI::Minimal;

# 設定ファイル認識
require "./init.cgi";
my %cf = set_init();

# データ受理
CGI::Minimal::max_read_size($cf{maxdata});
my $cgi = CGI::Minimal->new;
error('容量オーバー') if ($cgi->truncated);
my %in = parse_form($cgi);

if ($in{mode} eq 'reply') { reply_com(); }
blog_page();

#-----------------------------------------------------------
#  トップページ
#-----------------------------------------------------------
sub blog_page {
	$in{categ} =~ s/\D//g;
	$in{pg} =~ s/\D//g;
	
	# ページ数定義
	my $pg = $in{pg} || 0;
	
	# 設定ファイル認識
	my %set = read_set();
	
	# 書庫ファイル
	open(IN,"$cf{datadir}/archive.dat") or error("open err: archive.dat");
	my @archi = <IN>;
	close(IN);
	
	chomp @archi;
	
	# 変数初期化
	my ($caly,$calm,$pager,$nrflg,@list,@reply,%cal,%res);
	
	# 初期表示
	if (!$in{mode}) {
		
		my ($y,$m,$cnt) = split(/,/,$archi[0]);
		$caly = $y;
		$calm = $m;
		
		my ($i,%log,%num);
		open(IN,"$cf{datadir}/index.dat") or error("open err: index.dat");
		while(<IN>) {
			chomp;
			my ($no,$y,$m,$d,$tm,$cat) = split(/,/);
			
			# カテゴリモードのとき
			next if ($in{categ} > 0 && $in{categ} ne $cat);
			
			$i++;
			next if ($i < $pg + 1);
			last if ($i > $pg + $set{max_page});
			
			$log{"$y$m"}++;
			$num{$no}++;
		}
		close(IN);
		
		# ペイジャー作成
		$pager = make_pager($i,$pg,$set{max_page});
		
		# 月次データ読込
		for ( sort{ $b <=> $a } keys %log ) {
			/^(\d{4})(\d{2})/;
			open(IN,"$cf{datadir}/log/$1-$2.dat");
			while(<IN>) {
				my ($no,$y,$m,$d,$time,$cat,$res,$ttl,$com1,$com2,$com3,$ex1,$w1,$h1,$ex2,$w2,$h2,$ex3,$w3,$h3) = split(/<>/);
				
				# トップ表示記事
				if (defined $num{$no}) { push(@list,$_); }
			}
			close(IN);
			
			open(IN,"$cf{datadir}/com/$1-$2.dat");
			while(<IN>) {
				my ($no,$ent,$date,$name,$com,$up) = split(/<>/);
				
				# コメ数
				$res{$ent}++;
			}
			close(IN);
		}
	
	# 固定記事
	} elsif ($in{mode} eq "entry") {
		
		# 年月認識
		($caly,$calm) = $in{ym} =~ /^(\d{4})(\d{2})$/ ? ($1,$2)	: error("年月不正");
		
		# 当月記事
		my $i = 0;
		open(IN,"$cf{datadir}/log/$caly-$calm.dat") ;
		while(<IN>) {
			$i++;
			my ($no,$y,$m,$d,$time,$cat,$res,$ttl,$com1,$com2,$com3,$ex1,$w1,$h1,$ex2,$w2,$h2,$ex3,$w3,$h3) = split(/<>/);
			
			# カレンダ用
			$cal{$d}++;
			
			# 日指定の場合
			next if ($in{d} > 0 && $in{d} != $d);
			
			# 記事指定のとき
			if ($in{no} > 0) {
				if ($in{no} == $no) {
					if (!$res) { $nrflg++; } # レス無し
					push(@list,$_);
					last;
				}
			
			# 記事指定以外
			} else {
				next if ($i < $pg + 1);
				next if ($i > $pg + $set{max_page});
				
				push(@list,$_);
			}
		}
		close(IN);
		
		# ペイジャー作成
		$pager = make_pager($i,$pg,$set{max_page});
		
		# 当月返信
		open(IN,"$cf{datadir}/com/$caly-$calm.dat");
		while(<IN>) {
			my ($no,$ent,$date,$name,$com,$ip) = split(/<>/);
			$res{$ent}++;
			
			# 該当レス
			if ($in{no} == $ent) {
				push(@reply,$_);
			}
		}
		close(IN);
	}
	
	open(IN,"$cf{tmpldir}/blog.html") or error("open err: blog.html");
	my $tmpl = join('',<IN>);
	close(IN);
	
	# 記事指定時
	if ($in{mode} eq 'entry' && $in{no} && !$nrflg) {
		$tmpl =~ s/!index_cgi!/$cf{index_cgi}/g;
		$tmpl =~ s/!ym!/$in{ym}/g;
		$tmpl =~ s/!num!/$in{no}/g;
	} else {
		$tmpl =~ s|<!-- reply -->.+?<!-- /reply -->||s;
		$tmpl =~ s|<!-- form -->.+?<!-- /form -->||s;
	}
	my $prof_img = $set{prof_image}
			? qq|<p><img src="$cf{uplurl}/$set{prof_image}" style="width:$set{prof_img_w}px" alt="プロフ"></p>| : '';
	
	my $copy;
	$tmpl =~ s/!(index_cgi|cmnurl)!/$cf{$1}/g;
	$tmpl =~ s/!(title|subtitle|max_res|profile)!/$set{$1}/g;
	$tmpl =~ s|<!-- prof_image -->|$prof_img|g;
	$tmpl =~ s|<!-- post -->(.+?)<!-- /post -->|put_post($1,\@list,\%res,\%set)|se;
	$tmpl =~ s|<!-- entry -->(.+?)<!-- /entry -->|put_entry($1)|se;
	$tmpl =~ s|<!-- reply -->(.+?)<!-- /reply -->|put_reply($1,\@reply)|se;
	$tmpl =~ s|<!-- categ -->(.+?)<!-- /categ -->|put_categ($1,$set{categ})|se;
	$tmpl =~ s|<!-- calen -->(.+?)<!-- /calen -->|put_calen($1,$caly,$calm,\%cal,\@archi)|se;
	$tmpl =~ s|<!-- archive -->(.+?)<!-- /archive -->|put_archive($1,\@archi)|se;
	$tmpl =~ s/<!-- pager -->/$pager/g;
	my $copy = $tmpl =~ s|<!-- copy -->|- <a href="https://www.kent-web.com/">Smart Blog</a> -| && 1;
	
	print "Content-type: text/html; charset=utf-8\n\n";
	print $copy ? $tmpl : footer($tmpl);
	exit;
}

#-----------------------------------------------------------
#  メイン記事
#-----------------------------------------------------------
sub put_post {
	my ($loop,$post,$res,$set) = @_;
	
	my %cat;
	for ( split(/,/,$$set{categ}) ) {
		my ($key,$val) = split(/:/);
		
		$cat{$key} = $val;
	}
	
	my $ret;
	for (@$post) {
		my ($no,$y,$m,$d,$time,$cat,$rep,$sub,$com1,$com2,$com3,$ex1,$w1,$h1,$ex2,$w2,$h2,$ex3,$w3,$h3) = split(/<>/);
		$com1 = chg_tag($com1);
		if ($ex1) {
			my ($w,$h) = resize($w1,$h1,$$set{max_img_w},$$set{max_img_h});
			my $style = 'width:' . $w . 'px;height:' . $h . 'px';
			$com1 = qq|<a href="$cf{uplurl}/$no-1$ex1"><img src="$cf{uplurl}/$no-1$ex1" alt="$sub" style="$style"></a><div class="message">$com1</div>\n|;
		}
		
		# 続きリンク
		if (!($in{mode} eq "entry" && $in{no}) && ($com2 or $com3 or $ex2 or $ex3)) {
			$com1 .= qq|<div class="continue"><a href="$cf{index_cgi}?mode=entry&ym=$y$m&no=$no">続きを見る</a></div>\n|;
		}
		
		# 固定記事
		if ($in{mode} eq "entry" && $in{no}) {
			if ($ex2) {
				my ($w,$h) = resize($w2,$h2,$$set{max_img_w},$$set{max_img_h});
				my $style = 'width:' . $w . 'px;height:' . $h . 'px';
				$com1 .= qq|<a href="$cf{uplurl}/$no-2$ex2"><img src="$cf{uplurl}/$no-2$ex2" alt="$sub" style="$style"></a>\n|;
			}
			if ($com2) {
				$com2 = chg_tag($com2);
				$com1 .= qq|<div class="message">$com2</div>\n|;
			}
			if ($ex3) {
				my ($w,$h) = resize($w3,$h3,$$set{max_img_w},$$set{max_img_h});
				my $style = 'width:' . $w . 'px;height:' . $h . 'px';
				$com1 .= qq|<a href="$cf{uplurl}/$no-3$ex3"><img src="$cf{uplurl}/$no-3$ex3" alt="$sub" style="$style"></a>\n|;
			}
			if ($com3) {
				$com3 = chg_tag($com3);
				$com1 .= qq|<div class="message">$com3</div>\n|;
			}
		}
		
		my $tmp = $loop;
		$tmp =~ s|<!-- com_link -->.+?<!-- /com_link -->||s if (!$rep); # コメ無し
		$tmp =~ s/!sub!/$sub/g;
		$tmp =~ s/!message!/$com1/g;
		$tmp =~ s|!date!|$y/$m/$d $time|g;
		$tmp =~ s/!plink!/$cf{index_cgi}?mode=entry&ym=$y$m&no=$no/g;
		$tmp =~ s/!comment!/$cf{index_cgi}?mode=entry&ym=$y$m&no=$no#comment/g;
		$tmp =~ s/!res_cnt!/defined $$res{$no} ? $$res{$no} : 0/e;
		$tmp =~ s|!categ!|$cat ne '' ? qq!<a href="$cf{index_cgi}?categ=$cat">$cat{$cat}</a>! : '未分類'|e;
		
		$ret .= $tmp;
	}
	return $ret;
}

#-----------------------------------------------------------
#  コメント記事
#-----------------------------------------------------------
sub put_reply {
	my ($loop,$reply) = @_;
	
	my ($i,$ret);
	for (@$reply) {
		$i++;
		my ($no,$ent,$date,$name,$com) = split(/<>/);
		
		my $tmp = $loop;
		$tmp =~ s/!number!/$i/g;
		$tmp =~ s/!num!/$no/g;
		$tmp =~ s/!name!/$name/g;
		$tmp =~ s|!date!|$date|g;
		$tmp =~ s/!comment!/$com/g;
		
		$ret .= $tmp;
	}
	return $ret;
}

#-----------------------------------------------------------
#  エントリー記事
#-----------------------------------------------------------
sub put_entry {
	my ($loop) = @_;
	
	my $ret;
	open(IN,"$cf{datadir}/entry.dat");
	while(<IN>) {
		chomp;
		my ($no,$y,$m,$d,$sub) = split(/,/);
		
		my $tmp = $loop;
		$tmp =~ s/#/$cf{index_cgi}?mode=entry&ym=$y$m&no=$no/g;
		$tmp =~ s/!entry!/$sub/g;
		$tmp =~ s|!date!|$m/$d|g;
		
		$ret .= $tmp;
	}
	close(IN);
	
	return $ret;
}

#-----------------------------------------------------------
#  カテゴリ一覧
#-----------------------------------------------------------
sub put_categ {
	my ($loop,$categ) = @_;
	
	my %cnt;
	open(IN,"$cf{datadir}/categ.dat");
	while(<IN>) {
		chomp;
		my ($key,$val) = split(/,/);
		
		$cnt{$key} = $val;
	}
	close(IN);
	
	my $ret;
	for ( split(/,/,$categ) ) {
		my ($key,$val) = split(/:/);
		
		my $tmp = $loop;
		$tmp =~ s/!categ!/$val/g;
		$tmp =~ s|#|$cf{index_cgi}?categ=$key|g;
		$tmp =~ s/!count!/$cnt{$key} ? $cnt{$key} : 0/eg;
		
		$ret .= $tmp;
	}
	
	return $ret;
}

#-----------------------------------------------------------
#  メニューカレンダー
#-----------------------------------------------------------
sub put_calen {
	my ($calen,$y,$m,$cal,$arcv) = @_;
	
	# アーカイブ
	my ($pt,$next,$prev);
	my $i = 0;
	for (@$arcv) {
		my ($yr,$mon,$cnt) = split(/,/);
		
		if ($y == $yr && $m == $mon) {
			$pt = $i;
		}
		$i++;
	}
	# 当月のみ
	if ($i == 0 or $i == 1) {
		$prev = '&nbsp;';
		$next = '&nbsp;';
	
	# TOP
	} elsif ($pt == 0) {
		my ($yr,$mon,$cnt) = split(/,/,$$arcv[1]);
		
		$prev = qq|<a href="$cf{index_cgi}?mode=entry&ym=$yr$mon">&laquo;</a>|;
		$next = '&nbsp;';
	
	# 最終
	} elsif ($pt == $#{$arcv}) {
		my ($yr,$mon,$cnt) = split(/,/,$$arcv[$#{$arcv} - 1]);
		
		$prev = '&nbsp;';
		$next = qq|<a href="$cf{index_cgi}?mode=entry&ym=$yr$mon">&raquo;</a>|;
	
	# 中間
	} else {
		my ($yr,$mon,$cnt) = split(/,/,$$arcv[$pt + 1]);
		$prev = qq|<a href="$cf{index_cgi}?mode=entry&ym=$yr$mon">&laquo;</a>|;
		
		my ($yr,$mon,$cnt) = split(/,/,$$arcv[$pt - 1]);
		$next = qq|<a href="$cf{index_cgi}?mode=entry&ym=$yr$mon">&raquo;</a>|;
	}
	
	if (%$cal == 0) {
		open(IN,"$cf{datadir}/log/$y-$m.dat");
		while(<IN>) {
			my ($no,$y,$m,$d,$time,$cat,$rep,$sub,$com1,$com2,$com3,$ex1,$w1,$h1,$ex2,$w2,$h2,$ex3,$w3,$h3) = split(/<>/);
			
			$$cal{$d}++;
		}
		close(IN);
	}
	
	my ($top,$cel,$bot) = $calen =~ m|(.+)<!-- days -->(.+?)<!-- /days -->(.+)|s
			? ($1,$2,$3)
			: return 'bad calendar';
	
	for ($top,$bot) {
		s/!year!/$y/g;
		s/!month!/$m/g;
		s/<!-- prev -->/$prev/g;
		s/<!-- next -->/$next/g;
	}
	
	# 初日の曜日を取得
	my $top_w = get_week($y,$m,1);
	
	# 当月の末日を求める
	my $last = last_day($y,$m);
	
	# 第1週：月初のブランクを記述
	my $tmp = $cel;
	my ($ret,$flg);
	my $day = 0;
	for my $w (0 .. 6) {
		if ($w == $top_w) {
			$flg++;
		}
		if ($flg) {
			$day++;
			my $d = sprintf("%02d",$day);
			
			# 記事あり
			if (defined $$cal{$d}) {
				$tmp =~ s|!$w!|<a href="$cf{index_cgi}?mode=entry&ym=$y$m&d=$day">$day</a>|;
			
			# 記事なし
			} else {
				$tmp =~ s/!$w!/$day/;
			}
		} else {
			$tmp =~ s/!$w!/&nbsp;/;
		}
	}
	$ret .= $tmp;
	
	# 第2週以降
	my $flg;
	while (!$flg) {
		my $tmp = $cel;
		for my $w (0 .. 6) {
			if (!$flg) {
				$day++;
				my $d = sprintf("%02d",$day);
				
				# 記事あり
				if (defined $$cal{$d}) {
					$tmp =~ s|!$w!|<a href="$cf{index_cgi}?mode=entry&ym=$y$m&d=$day">$day</a>|;
				
				# 記事なし
				} else {
					$tmp =~ s/!$w!/$day/;
				}
			} else {
				$tmp =~ s/!$w!/&nbsp;/;
			}
			if ($day >= $last) {
				$flg = 1;
			}
		}
		$ret .= $tmp;
	}
	return $top . $ret . $bot;
}

#-----------------------------------------------------------
#  メニューアーカイブ
#-----------------------------------------------------------
sub put_archive {
	my ($loop,$archi) = @_;
	
	my $ret;
	for (@$archi) {
		my ($y,$m,$cnt) = split(/,/);
		
		my $tmp = $loop;
		$tmp =~ s/!year!/$y/g;
		$tmp =~ s/!month!/$m/g;
		$tmp =~ s/!count!/$cnt/g;
		$tmp =~ s/#/$cf{index_cgi}?mode=entry&ym=$y$m/g;
		
		$ret .= $tmp;
	}
	return $ret;
}

#-----------------------------------------------------------
#  月の末日
#-----------------------------------------------------------
sub last_day {
	my ($yy,$mm) = @_;
	
	return (31,28,31,30,31,30,31,31,30,31,30,31) [$mm - 1]
		+ ($mm == 2 && (($yy % 4 == 0 && $yy % 100 != 0) || $yy % 400 == 0));
}

#-----------------------------------------------------------
#  週算出
#-----------------------------------------------------------
sub get_week {
	my ($yy,$mm,$dd) = @_;
	
	if ($mm == 1 || $mm == 2) {
		$yy--;
		$mm += 12;
	}
	int( $yy + int($yy / 4) - int($yy / 100) + int($yy / 400) + int((13 * $mm + 8) / 5) + $dd ) % 7;
}

#-----------------------------------------------------------
#  フッター
#-----------------------------------------------------------
sub footer {
	my $tmpl = shift;
	
	# 著作権表記（削除・改変禁止）
	my $copy = <<EOM;
<p style="margin-top:2em;text-align:center;font-size:10px;">
	- <a href="https://www.kent-web.com/">Smart Blog</a> -
</p>
EOM

	my $ret;
	if ($tmpl =~ /(.+)(<\/body[^>]*>.*)/si) {
		$ret .= "$1$copy$2\n";
	} else {
		$ret .= "$tmpl$copy\n";
		$ret .= "</body></html>\n";
	}
	return $ret;
}

#-----------------------------------------------------------
#  ペイジャー作成
#-----------------------------------------------------------
sub make_pager {
	my ($i,$pg,$max) = @_;
	
	my $next = $pg + $max;
	my $prev = $pg - $max;
	
	# 引数
	my $param;
	if ($in{mode}) {
		$param .= "&mode=$in{mode}&ym=$in{ym}";
	}
	if ($in{categ}) {
		$param .= "&categ=$in{categ}";
	}
	
	my ($ret,$flg);
	if ($prev >= 0) {
		$flg++;
		$ret .= qq|<li><a href="$cf{index_cgi}?pg=$prev$param">PREV</a></li>\n|;
	}
	if ($next < $i) {
		$flg++;
		$ret .= qq|<li><a href="$cf{index_cgi}?pg=$next$param">NEXT</a></li>\n|;
	}
	return $flg ? qq|<ul>$ret</ul>| : '';
}

#-----------------------------------------------------------
#  エラー画面
#-----------------------------------------------------------
sub error {
	my $err = shift;
	
	open(IN,"$cf{tmpldir}/error.html") or die;
	my $tmpl = join('',<IN>);
	close(IN);
	
	$tmpl =~ s/!error!/$err/g;
	$tmpl =~ s/!cmnurl!/$cf{cmnurl}/g;
	
	print "Content-type: text/html; charset=utf-8\n\n";
	print $tmpl;
	exit;
}

#-----------------------------------------------------------
#  完了画面
#-----------------------------------------------------------
sub message {
	my $msg = shift;
	
	open(IN,"$cf{tmpldir}/mesg.html") or die;
	my $tmpl = join('',<IN>);
	close(IN);
	
	$tmpl =~ s/!message!/$msg/;
	$tmpl =~ s/!(index_cgi|cmnurl)!/$cf{$1}/g;
	
	print "Content-type: text/html; charset=utf-8\n\n";
	print $tmpl;
	exit;
}

#-----------------------------------------------------------
#  タグ復元
#-----------------------------------------------------------
sub chg_tag {
	local($_) = @_;
	
	s/&gt;/>/g;
	s/&lt;/</g;
	s/&quot;/"/g;
	s/&amp;/&/g;
	s/&#39;/'/g;
	
	return $_;
}

#-----------------------------------------------------------
#  画像リサイズ
#-----------------------------------------------------------
sub resize {
	my ($w,$h,$max_img_w,$max_img_h) = @_;
	
	# 画像表示縮小
	if ($w > $max_img_w || $h > $max_img_h) {
		my $w2 = $max_img_w / $w;
		my $h2 = $max_img_h / $h;
		my $key;
		if ($w2 < $h2) { $key = $w2; } else { $key = $h2; }
		$w = int ($w * $key) || 1;
		$h = int ($h * $key) || 1;
	}
	return ($w,$h);
}

#-----------------------------------------------------------
#  コメント投稿
#-----------------------------------------------------------
sub reply_com {
	if ($in{no} !~ /^\d+$/ or $in{ym} !~ /^\d{6}$/) { error("投稿できません"); }
	$in{name} =~ s/<br>//g;
	$in{comment} =~ s/(<br>)+$//;
	if ($in{name} eq '' or $in{comment} eq '') { error("名前またはコメントが未入力です"); }
	
	# 設定ファイル
	my %set = read_set();
	
	# 実行
	if ($in{job} eq 'reg') {
		# セッションチェック
		check_ses();
		
		$in{comment} =~ s/&lt;br&gt;/<br>/g;
	
	# 確認画面
	} else {
		# 最大レスチェック
		check_max_res($set{max_res});
		
		my $ses = make_capt();
		
		open(IN,"$cf{tmpldir}/comment.html") or die;
		my $tmpl = join('',<IN>);
		close(IN);
		
		$tmpl =~ s/!title!/$set{title}/g;
		$tmpl =~ s/!name!/$in{name}/g;
		$tmpl =~ s/!comment!/$in{comment}/g;
		$tmpl =~ s/!no!/$in{no}/g;
		$tmpl =~ s/!ym!/$in{ym}/g;
		$tmpl =~ s/!(index_cgi|cmnurl)!/$cf{$1}/g;
		$tmpl =~ s/!ses!/$ses/g;
		
		if ($cf{use_captcha} == 0) {
			$tmpl =~ s|<!-- captcha -->.+<!-- /captcha -->||s;
		} else {
			$tmpl =~ s/!captcha!/<img src="$cf{captcha_cgi}?$ses" alt="画像認証">/g;
		}
		
		print "Content-type: text/html; charset=utf-8\n\n";
		print $tmpl;
		exit;
	}
	# 記事タイトル
	my $ym = $in{ym} =~ /^(\d{4})(\d{2})/ && "$1-$2";
	my ($day,$ttl,$flg);
	open(IN,"+< $cf{datadir}/log/$ym.dat");
	while(<IN>) {
		my ($no,$y,$m,$d,$time,$cat,$rep,$sub,$com1,$com2,$com3,$ex1,$w1,$h1,$ex2,$w2,$h2,$ex3,$w3,$h3) = split(/<>/);
		
		if ($in{no} == $no) {
			$ttl = $sub;
			$day = $d;
			if (!$rep) { $flg++; }
			last;
		}
	}
	close(IN);
	
	# レス無し
	if ($flg) { error("この記事にはコメントできません"); }
	
	# ID作成
	my @wd = (0 .. 9, 'a' .. 'z', 'A' .. 'Z');
	my $id;
	for (1 .. 10) { $id .= $wd[int(rand(@wd))]; }
	$id = time . $id;
	
	# 時間取得
	my ($min,$hr,$day,$mon,$yr) = (localtime())[1..5];
	my $date = sprintf("%04d/%02d/%02d %02d:%02d",$yr+1900,$mon+1,$day,$hr,$min);
	
	# 一時ファイル保存
	my $addr = $ENV{REMOTE_ADDR};
	open(DAT,">> $cf{datadir}/temp.dat");
	eval "flock(DAT,2);";
	print DAT "$id<>$in{ym}<>$in{no}<>$date<>$in{name}<>$in{comment}<>$addr<>$ttl<>$day\n";
	close(DAT);
	
	# メール通知
	mail_to($date,$ttl,$addr) if ($cf{mailing} == 1);
	
	# 完了
	message("投稿を受理しました。管理者が承認されましたら表示されます。");
}

#-----------------------------------------------------------
#  コメントレス確認
#-----------------------------------------------------------
sub check_max_res {
	my $max = shift;
	
	my $ym = $in{ym} =~ /^(\d{4})(\d{2})/ && "$1-$2";
	my ($i,$flg);
	open(IN,"$cf{datadir}/com/$ym.dat");
	while(<IN>) {
		my ($no,$ent,$date,$name,$com,$ip) = split(/<>/);
		
		if ($in{no} == $ent) {
			$i++;
		}
		if ($i >= $max) {
			$flg++;
			last;
		}
	}
	close(IN);
	
	if ($flg) { error("コメント数が規定数に達しているため投稿できません"); }
}

#-----------------------------------------------------------
#  画像認証作成
#-----------------------------------------------------------
sub make_capt {
	# 画像認証用
	my @dig = (0 .. 9);
	my $dig;
	for (1 .. $cf{cap_len}) { $dig .= $dig[int(rand(@dig))]; }
	
	# セッション文字
	my @wd = (0 .. 9, 'a' .. 'z', 'A' .. 'Z', '_');
	my $ses;
	for (1 .. 25) { $ses .= $wd[int(rand(@wd))]; }
	
	# セッションファイル記録
	my $now = time;
	my @log;
	open(DAT,"+< $cf{datadir}/conf.cgi") or error("open err: conf.cgi");
	eval "flock(DAT,2);";
	while(<DAT>) {
		my ($time,$rand,$fig) = split(/\t/);
		next if ($now - $time > $cf{cap_time}*60);
		
		push(@log,$_);
	}
	unshift(@log,"$now\t$ses\t$dig\t\n");
	seek(DAT,0,0);
	print DAT @log;
	truncate(DAT,tell(DAT));
	close(DAT);
	
	return $ses;
}

#-----------------------------------------------------------
#  セッション確認
#-----------------------------------------------------------
sub check_ses {
	my $now = time;
	my ($flg,@log);
	open(DAT,"+< $cf{datadir}/conf.cgi");
	eval "flock(DAT,2);";
	while(<DAT>) {
		chomp;
		my ($time,$rand,$fig) = split(/\t/);
		next if ($now - $time > $cf{cap_time}*60);
		
		if ($in{ses} eq $rand) {
			$flg = 1;
			if ($cf{use_captcha} > 0 && $fig ne $in{captcha}) {
				$flg = -1;
				last;
			}
			next;
		}
		push(@log,"$_\n");
	}
	if ($flg == -1) {
		close(DAT);
		error("画像認証できません");
	}
	seek(DAT,0,0);
	print DAT @log;
	truncate(DAT,tell(DAT));
	close(DAT);
	
	if (!$flg) { error("セッション不良のため、再度投稿し直してください"); }
}

#-----------------------------------------------------------
#  メール送信
#-----------------------------------------------------------
sub mail_to {
	my ($date,$ttl,$addr) = @_;
	
	# 件名をMIMEエンコード
	require "./lib/jacode.pl";
	my $msub = mime_unstructured_header("$ttlへのコメント");
	
	# コメント内の改行復元
	my $com = $in{comment};
	$com =~ s|<br>|\n|g;
	$com =~ s/&lt;/>/g;
	$com =~ s/&gt;/</g;
	$com =~ s/&quot;/"/g;
	$com =~ s/&amp;/&/g;
	$com =~ s/&#39;/'/g;
	
	# メール本文を定義
	my $body = <<EOM;
「$ttl」へコメント投稿がありました。
管理画面にて、承認/削除を行ってください。

投稿日：$date
IP情報：$addr
お名前：$in{name}

$com
EOM

	# JISコード変換
	my $mbody;
	for my $tmp ( split(/\n/,$body) ) {
		jcode::convert(\$tmp,'jis','utf8');
		$mbody .= "$tmp\n";
	}
	
	# メールアドレスがない場合は管理者メールに置き換え
	$in{email} ||= $cf{mailto};
	
	# sendmailコマンド
	my $scmd = "$cf{sendmail} -t -i";
	if ($cf{sendm_f}) { $scmd .= " -f $in{email}"; }
	
	# 送信
	open(MAIL,"| $scmd") or error("送信失敗");
	print MAIL "To: $cf{mailto}\n";
	print MAIL "From: $cf{mailto}\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: $cf{version}\n\n";
	print MAIL "$mbody\n";
	close(MAIL);
}

#-----------------------------------------------------------
#  mimeエンコード
#  [quote] http://www.din.or.jp/~ohzaki/perl.htm#JP_Base64
#-----------------------------------------------------------
sub mime_unstructured_header {
  my $oldheader = shift;
  jcode::convert(\$oldheader,'euc','utf8');
  my ($header,@words,@wordstmp,$i);
  my $crlf = $oldheader =~ /\n$/;
  $oldheader =~ s/\s+$//;
  @wordstmp = split /\s+/, $oldheader;
  for ($i = 0; $i < $#wordstmp; $i++) {
    if ($wordstmp[$i] !~ /^[\x21-\x7E]+$/ and
	$wordstmp[$i + 1] !~ /^[\x21-\x7E]+$/) {
      $wordstmp[$i + 1] = "$wordstmp[$i] $wordstmp[$i + 1]";
    } else {
      push(@words, $wordstmp[$i]);
    }
  }
  push(@words, $wordstmp[-1]);
  foreach my $word (@words) {
    if ($word =~ /^[\x21-\x7E]+$/) {
      $header =~ /(?:.*\n)*(.*)/;
      if (length($1) + length($word) > 76) {
	$header .= "\n $word";
      } else {
	$header .= $word;
      }
    } else {
      $header = add_encoded_word($word, $header);
    }
    $header =~ /(?:.*\n)*(.*)/;
    if (length($1) == 76) {
      $header .= "\n ";
    } else {
      $header .= ' ';
    }
  }
  $header =~ s/\n? $//mg;
  $crlf ? "$header\n" : $header;
}
sub add_encoded_word {
  my ($str, $line) = @_;
  my $result;
  my $ascii = '[\x00-\x7F]';
  my $twoBytes = '[\x8E\xA1-\xFE][\xA1-\xFE]';
  my $threeBytes = '\x8F[\xA1-\xFE][\xA1-\xFE]';
  while (length($str)) {
    my $target = $str;
    $str = '';
    if (length($line) + 22 +
	($target =~ /^(?:$twoBytes|$threeBytes)/o) * 8 > 76) {
      $line =~ s/[ \t\n\r]*$/\n/;
      $result .= $line;
      $line = ' ';
    }
    while (1) {
      my $encoded = '=?ISO-2022-JP?B?' .
      b64encode(jcode::jis($target,'euc','z')) . '?=';
      if (length($encoded) + length($line) > 76) {
	$target =~ s/($threeBytes|$twoBytes|$ascii)$//o;
	$str = $1 . $str;
      } else {
	$line .= $encoded;
	last;
      }
    }
  }
  $result . $line;
}
# [quote] http://www.tohoho-web.com/perl/encode.htm
sub b64encode {
    my $buf = shift;
    my ($mode,$tmp,$ret);
    my $b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
                . "abcdefghijklmnopqrstuvwxyz"
                . "0123456789+/";
	
    $mode = length($buf) % 3;
    if ($mode == 1) { $buf .= "\0\0"; }
    if ($mode == 2) { $buf .= "\0"; }
    $buf =~ s/(...)/{
        $tmp = unpack("B*", $1);
        $tmp =~ s|(......)|substr($b64, ord(pack("B*", "00$1")), 1)|eg;
        $ret .= $tmp;
    }/eg;
    if ($mode == 1) { $ret =~ s/..$/==/; }
    if ($mode == 2) { $ret =~ s/.$/=/; }
    
    return $ret;
}

