#!/usr/bin/perl

;# -------------------------------------------------------------------
;# Copyright
;#
;#		Script   :  Mini Thread
;#		Homepage :  Flash CGI
;#		URL      :  http://www.flashcgi.net/
;#		Mail     :  webmaster@flashcgi.net
;#		Type     :  Free Ware(May Convert)
;# -------------------------------------------------------------------
;#
;# 設定ファイル名
;; require 'config.cgi';
;#
;# -------------------------------------------------------------------
;# 書式
;#
;# q=検索ワード(&tr=対象スレッド&st=開始位置&no=表示数&op=and/or&tg=検索対象)
;# -------------------------------------------------------------------
;#
;; &main;
;#
;#
;# 検索画面
;#
sub main {
	#
	# 引数を受け取ります
	#
	my $QUERY_DATA = $ENV{'QUERY_STRING'};
	#
	# サイズチェック
	#
	&error(__LINE__,'検索ワードが長すぎです') if (length($QUERY_DATA) > $sobig);
	#
	# デコード
	#
	my @pairs = split(/&/,$QUERY_DATA);
	my $hold;
	foreach $pair (@pairs) {
		my ($name, $value) = split(/=/, $pair);
		$hold = $value if ($name eq 'q');
		$value =~ tr/+/ /;
		$value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg;
		$value =~ s/[\x00-\x0d\x10-\x1a\x1c-\x1f]+//g;
		$FORM{$name} = $value;
	}
	#
	# 変数取得
	#
	my $bbs = $FORM{'b'};
	my $no  = $FORM{'no'};
	my $q   = $FORM{'q'};
	my $tg  = $FORM{'tg'};
	#
	# 検索対象の引継ぎ
	#
	if (&checkBbs($bbs)) {
		&readsubset($pluspath.$bbs.'/'.$subsetcgi);
	} elsif (!$allboard) {
		&error(__LINE__,'板が見つかりません');
	}
	#
	# 圧縮
	#
	&encode();
	#
	# HTML
	#
	print '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">';
	print '<html><head>';
	print '<title>検索画面</title>';
	print '<meta http-equiv="Content-Type" content="text/html; charset=x-sjis">';
	print $search_style;
	print '</head>';
	print '<body  bgcolor="#C8005A" text="'.$search_text.'" link="'.$search_link.'" vlink="'.$search_vlink.'" alink="'.$search_alink.'">';
	print '<a href="'.$pluspath.$bbs.'/'.$indexfile.'">■掲示板に戻る■</a>';
	print '<form action="'.$searchcgi.'" method=get>';
	print '<input type=hidden name=ac value=1>';
	print '<input type=hidden name=b value="'.$bbs.'">';
	print '<table border=0 width="'.$search_width.'" cellspacing=0 cellpadding='.$search_line.' bgcolor="'.$search_top.'" align=center><tr><td>';
	print '<table width=100% border=0 cellspacing=0 cellpadding=5><tr><td width=20%>';
	print '<b><font color="'.$search_font.'">検索条件</font></b></td>';
	#
	# XSS対策
	#
	$q =~ s/&/&amp;/g;
	$q =~ s/</&lt;/g;
	$q =~ s/>/&gt;/g;
	$q =~ s/"/&quot;/g; #"
	print '<td colspan=2 nowrap><input type=text name=q value="'.$q.'"> ';
	print '<select name=no>';
	#
	# 検索数の引継ぎ
	#
	print '<option value=10';
	print ' selected' if ($no == 10);
	print '>10 件ずつ';
	print '<option value=20';
	print ' selected' if ($no != 30 and $no != 50 and $no != 100);
	print '>20 件ずつ';
	print '<option value=30';
	print ' selected' if ($no == 30);
	print '>30 件ずつ';
	print '<option value=50';
	print ' selected' if ($no == 50);
	print '>50 件ずつ';
	print '<option value=100';
	print ' selected' if ($no == 100);
	print '>100 件ずつ';
	print '</select> ';
	print '<input type=submit name=s value=" 検索 "></td></tr>';
	print '<tr bgcolor="'.$search_bottom.'"><td rowspan=5 valign=top nowrap><b>オプション</b></td>';
	print '<td nowrap><br><b>検索対象</b><br></td><td width=30% nowrap><br>';
	print '<select name=tg>';
	print '<option value=lg';
	print ' selected' if ($tg eq 'lg');
	print '>内容';
	print '<option value=nm';
	print ' selected' if ($tg eq 'nm');
	print '>名前';
	print '<option value=ml';
	print ' selected' if ($tg eq 'ml');
	print '>メール';
	print '<option value=ur';
	print ' selected' if ($tg eq 'ur');
	print '>ＵＲＬ';
	print '<option value=id';
	print ' selected' if ($tg eq 'id');
	print '>日付/ID';
	print '<option value=tt';
	print ' selected' if ($tg eq 'tt');
	print '>タイトル';
	print '<option value=al';
	print ' selected' if ($tg eq 'al');
	print '>すべて　　';
	print '</select>';
	print '</td></tr><tr bgcolor="'.$search_bottom.'"><td nowrap>';
	print '<b>検索範囲</b>（板・スレッド）<br></td><td nowrap>';
	#
	# 板リストを表示
	# 隔離板の場合、問題
	#
	if ($allboard and !&checkBbs($bbs,1)) {
		my @BBS = &bbsList();
		my $half= @BBS/2;
		my $i;
		print '<select name=b>';
		while ($i < $half) {
			print '<option value="'.$BBS[$i].'"';
			print ' selected' if ($bbs eq $BBS[$i]);
			print '>'.$BBS[$i+$half];
			++$i;
		}
		print '</select>';
		print '</td></tr><tr bgcolor="'.$search_bottom.'"><td>　</td><td>';
	}
	print '<select name=tr>';
	#
	# スレッド検索
	#
	if ($FORM{'tr'} =~ /^[0-9]+$/) {
		print '<option value='.$FORM{'tr'}.' selected>'.$FORM{'tr'}.'.dat';
	}
	print '<option value="">すべて　　';
	print '</select>';
	print '</td></tr><tr bgcolor="'.$search_bottom.'"><td>';
	print '<b>すべての</b>キーワードを含む<br><b>いずれかの</b>キーワードを含む</td><td nowrap>';
	#
	# or,andの引継ぎ
	#
	print '<input type=radio name=or value=0';
	print ' checked' if (!$FORM{'or'});
	print '>and検索<br>';
	print '<input type=radio name=or value=1';
	print ' checked' if ($FORM{'or'});
	print '> or検索</td></tr>';
	print '<tr bgcolor="'.$search_bottom.'"><td nowrap><b>本文</b>を表\示する。<br>';
	print '</td><td nowrap><input type=checkbox name=vw value=1';
	print ' checked'if ($FORM{'vw'} or $no eq '');
	print '> on';
	print '</td></tr></table></td></tr></table></form>';
	#
	# 検索処理
	#
	if ($FORM{'ac'} eq '') {
		&copyright;
	} else {
		&search(\%FORM,$bbs,$hold);
	}
	#
	# 著作と終端
	#
	&copyright;
}
;#
;# 検索
;#
sub search {
	#
	# 受信
	#
	my $FORM = shift;
	my $bbs  = shift;
	my $hold = shift;
	my ($sthread,$sres,$flag,$result,@FILE);
	my $dir = $pluspath.$bbs.'/'.$savedir;
	#
	# デコードする
	#
	require 'write.pl';
	&decode(' 検索 ',$$FORM{'s'},\%FORM);
	#
	# エスケープ
	#
	my $keyword = $$FORM{'q'};
	my $number = $$FORM{'no'};
	my $target = $$FORM{'tg'};
	my $thread = $$FORM{'tr'};
	my $start = $$FORM{'st'};
	$keyword =~ s/　/ /g;
	$keyword =~ s/ +/ /g;
	$keyword =~ tr/ /\0/s;
	#
	# フォームチェック
	#
	if ($keyword eq '') {
		&searcherror('検索ワードが入力されていません');
	}
	if ($number eq '' or $number !~ /[0-9]{1,3}/) {
		$number = 20;
	}
	#
	# 汚染チェック
	#
	if ($start and $start !~ m|^([0-9]*/[0-9]*)$|) {
		&searcherror('不正なデータを受信しました');
	}
	#
	# 変換
	#
	my @keylist = split(/\0/,$keyword);
	#
	# 負荷が上がりそうなことははねる
	#
	if (@keylist > 10) {
		&searcherror('検索ワードが多すぎます');
	}
	foreach $k (@keylist) {
		&searcherror('検索ワードは2byte以上指定してください') if (length($k) <= 1);
	}
	#
	# 処理分岐
	#
	if (!$thread) {
		#
		# データファイル探索
		#
		opendir(DIR,$dir) or &searcherror('ディレクトリのオープンに失敗しました');
		@FILE = sort{ (stat($dir.$b))[9] <=> (stat($dir.$a))[9] } grep { /\.dat$/ } readdir(DIR);
		closedir(DIR);
	} else {
		#
		# 汚染チェック
		#
		if ($thread =~ /\D/) {
			&searcherror('不正なデータを受信しました');
		}
		if (-e $dir.$thread.'.dat') {
			push(@FILE,$thread.'.dat');
		}
	}
	#
	# 始まり
	#
	if (!$start) {
		$flag = 1;
	} else {
		($sthread,$sres) = split(/\//,$start);
	}
	#
	# 一個ずつ
	#
	foreach (@FILE) {
		#
		# スレッド
		#
		my $threadno = $_+0;
		my ($title,$resno);
		#
		# スキップチェック
		#
		if ($threadno == $sthread) {
			$flag = 1;
		}
		#
		# スキップ
		#
		if (!$flag) {
			next;
		}
		#
		# 読み込み
		#
		open(LOG,$dir.$_) or &searcherror($_.'のオープンに失敗しました');
		#
		# タイトル
		#
		$title = (split(/<>/,scalar(<LOG>)))[4];
		seek(LOG,0,0);
		#
		# レススキップ
		#
		if ($threadno == $sthread) {
			$resno = $sres;
			while($sres--) {
				<LOG>;
			}
		}
		#
		# 総当り
		#
		while(<LOG>) {
			#
			# リセット
			#
			my ($ok,$tgword);
			++$resno;
			#
			# 分割
			#
			my @TEMP = split(/<>/);
			#
			# 目標特定
			#
			if ($target eq 'nm') {
				$tgword = $TEMP[0];
			}
			elsif ($target eq 'ml') {
				$tgword = $TEMP[1];
			}
			elsif ($target eq 'ur') {
				$tgword = $TEMP[5];
			}
			elsif ($target eq 'id') {
				my $id;
				if ($idsalt) {
					if ($threeid ne '' and $TEMP[1] eq $threeid) {
						$id = '???';
					} else {
						$TEMP[2] =~ m|\d+/(\d+)/(\d+)|;
						$id = &idsum($1,$2,$TEMP[7]);
					}
					$tgword = $id;
				}
				$tgword .= $TEMP[2];
			}
			elsif ($target eq 'al') {
				$tgword = $_;
			}
			elsif ($target eq 'tt') {
				$tgword = $TEMP[4];
			}
			else {
				$target = 'lg';
				$tgword = $TEMP[3];
			}
			#
			# 複数検索対応
			#
			my $match;
			foreach $key (@keylist) {
				my $l = length($key);
				my $i = index($tgword,$key);
				if ($i != -1) {
					++$ok;
					++$match;
					$i += $l;
					while (($i=index($tgword,$key,$i)) != -1) {
						++$match;
						$i += $l;
					}
				}
			}
			#
			# or検索
			#
			if ($$FORM{'or'} and $ok > 0) {
				++$result;
				&srhtml(\@TEMP,\@keylist,$bbs,$match,$$FORM{'vw'},$title,$threadno,$resno,$target);
			}
			#
			# and検索
			#
			elsif ($ok == @keylist) {
				++$result;
				&srhtml(\@TEMP,\@keylist,$bbs,$match,$$FORM{'vw'},$title,$threadno,$resno,$target);
			}
			#
			# 終了判定
			#
			if ($result >= $number) {
				close(LOG);
				print '<br><div align=center><a href="'.$searchcgi.'?ac=1&q='.$hold.'&b='.$bbs.'&no='.$number.'&st=';
				print $threadno.'/'.$resno.'&or='.$$FORM{'or'}.'&tg='.$$FORM{'tg'}.'&vw='.$$FORM{'vw'}.'">続きを検索</a></div>';
				&copyright;
			}
			#
			# タイトル検索対応
			#
			if ($target eq 'tt') {
				last;
			}
		}
		#
		# ファイル閉じ
		#
		close(LOG);
	}
	#
	# ログの移動などによるエラー
	#
	if ($sthread and !$flag) {
		&searcherror('ログが消されたか移動された可能性があります');
	}
	#
	# 終了
	#
	if ($result == 0) {
		&searcherror('マッチしませんでした');
	}
}
;#
;# 結果表示html
;#
sub srhtml {
	#
	# 変数取得
	#
	my $TEMP = shift;
	my $key = shift;
	my $bbs = shift;
	my $match = shift;
	my $view = shift;
	my $title = shift;
	my $threadno = shift;
	my $resno = shift;
	my $target = shift;
	#
	# 全文表示か省略表示かで分岐
	#
	if ($view) {
		print '<table border=0 width="'.$search_width.'" cellspacing=0 cellpadding=1 bgcolor="'.$search_top.'" align=center><tr><td>';
		print '<table width=100% border=0 cellspacing=0 cellpadding=5><tr><td>';
		print '<a href='.$readcgi.'/'.$bbs.'/'.$threadno.'/l'.$newest.' target="'.$resulttg.'">';
		print '<font color="'.$search_font.'">'.$title.'</font></a>';
		print '<small>（ヒット数：'.$match.'）</small>';
		print '</td></tr>';
		&convertData($TEMP,'return'=>$pluspath,'link'=>$readcgi.'/'.$bbs.'/'.$threadno.'/','resno'=>$resno,'color'=>$search_nameclr,'count'=>$count,'print' => 0);
		#
		# 着色
		#
		if ($target eq 'lg' or $target eq 'al') {
			&coloring(\$$TEMP[3],$key);
		}
		print '<tr bgcolor="'.$search_bottom.'"><td><a href="'.$readcgi.'/'.$bbs.'/'.$threadno.'/'.$resno.'">'.$resno.'</a> ';
		print '名前：<font color="'.$read_nameclr.'"><b>'.$$TEMP[0].'</b></font> '.$$TEMP[2].' '.$$TEMP[5];
		print '<p>'.$$TEMP[3].'</p></td></tr>';
		print '</table></td></tr></table>';
	} else {
		print '<table border=0 width="'.$search_width.'" cellspacing=0 cellpadding=0 bgcolor="'.$search_top.'" align=center><tr><td>';
		print '<table width=100% border=0 cellspacing=1 cellpadding=5><tr bgcolor="'.$search_bottom.'"><td width=100%>';
		print '<a href="'.$readcgi.'/'.$bbs.'/'.$threadno.'/l'.$newest.'" target="'.$resulttg.'">'.$title.'</a></td><td nowrap>';
		print '<a href="'.$readcgi.'/'.$bbs.'/'.$threadno.'/'.$resno.'">閲覧</a>';
		print '</td></table></td></tr></table>';
	}
}
;#
;# マッチした文字を強調表示する
;#
sub coloring {
	#
	# 変数取得
	#
	my $str = shift;
	my $key = shift;
	my @key = @$key;
	return if (@key > 5);
	my $r_str;
	#
	# タグを区切りにして配列に代入する
	#
	my @str = split(/(<[^>]*>)/,$$str);
	$$str = '';
	while(@str) {
		my $tmp = shift(@str);
		foreach $k (@key) {
			my $l = length($k);
			my $t;
			while (($p=index($tmp,$k)) != -1) {
				$t .= substr($tmp,0,$p);
				$t .= '<b style="color:'.$searchcolor.';background-color:'.$searchcolor_back.'">'.$k.'</b>';
				$tmp = substr($tmp,$p+$l);
			}
			$tmp = $t.$tmp;
		}
		$$str .= $tmp.shift(@str);
	}
}
;#
;# エラー
;#
sub searcherror {
	print '<table border=0 width="'.$search_width.'" cellspacing=0 cellpadding=1 bgcolor="'.$search_top.'" align=center><tr><td>';
	print '<table width=100% border=0 cellspacing=0 cellpadding=5><tr bgcolor="'.$search_bottom.'"><td>';
	print $_[0].'</td></tr></table></td></tr></table>';
	&copyright;
}
