#!/usr/local/bin/perl
# このスクリプトを使用したいかなる損害に対して作者は一切の責任を負いません。
#
# *** 設置例 ***
# public_html/ ホームディレクトリ
# cgi-bin/ CGIディレクトリ
# puretree/ [777] PureTreeのディレクトリ
# puretree.cgi [755] PureTreeのCGIファイル
# puretree.css [644] PureTreeのCSSファイル
#
# 設置例では http://www.***.com/cgi-bin/puretree/puretree.cgi で実行することを想定
### 必須設定 ###
$PASS = 'donkiti'; #管理者のパスワード(4文字以上推奨)
### システム系 ###
$CGIURL = './puretree.cgi'; #CGIのURL(例:'http://www.***.com/cgi-bin/puretree/puretree.cgi')
$CSSURL = './puretree.css'; #CSSのURL(例:'http://www.***.com/cgi-bin/puretree/puretree.css')
$DATADIR = './'; #データの保存ディレクトリ(例:'./data/')
$LOGMAX = 100; #ログのファイルサイズの上限(KB) このサイズを超えると過去ログへ順次移動
### 表示系 ###
$TITLE = '実践!筋トレ相談室'; #ページのタイトル
$SUBJECTMAX = 60; #タイトルの表示上限(バイト)
$POPUPMAX = 100; #コメントのポップアップ上限(バイト) 0でポップアップ機能Off
$SUMMARYMAX = 200; #検索結果概要の表示上限(バイト)
$PAGEMAX = 5; #ページ選択の表示数(ページ)
$TREEOFFSET = 2; #各記事のツリー間隔(em) 0で記事のツリー表示Off
$UPTHREAD = 1; #更新されたスレッドの浮上(0:Off 1:On)
$DATE1 = 72; #新着時間1(時間) 投稿日時を赤表示 (注:赤表示されたスレッドが過去ログへ流れないよう設定調整してください。投稿規制が発動します)
$DATE2 = 24; #新着時間2(時間) 投稿日時を赤強調表示
### 制限系 ###
$COMMENTMAX = 1; #投稿サイズ上限(バイト)
$COMMENTLINEMAX = 100; #投稿行上限(行)
$AUTHORMAX = 1; #名前サイズ上限(バイト)
$POSTINTERVAL = 15; #連続投稿禁止時間(秒)
$EDITLIMIT = 60; #修正可能な時間(分) -99で修正機能Off
### 掲示板上部のレイアウト ###
$TOPLAYOUT = '
'."\n";
####################
# メイン
####################
%FORM = ();
%COOKIE = ();
{
#フォームの読み込み
if (my $error = &get_form(\%FORM)) {&message($error); exit(0);}
if (!defined($FORM{'mode'}) || ($FORM{'mode'} !~ /^\w+$/)) {$FORM{'mode'} = 'list';}
if (!defined($FORM{'page'}) || ($FORM{'page'} !~ /^\d{4}$/)) {$FORM{'page'} = '9999';}
if (!defined($FORM{'id'}) || ($FORM{'id'} !~ /^\d{10}$/)) {$FORM{'id'} = '';}
if (!defined($FORM{'parent'}) || ($FORM{'parent'} !~ /^\d{10}$/)) {$FORM{'parent'} = '';}
if (!defined($FORM{'root'}) || ($FORM{'root'} !~ /^\d{10}$/)) {$FORM{'root'} = '';}
if (!defined($FORM{'sid'}) || ($FORM{'sid'} =~ /\W/)) {$FORM{'sid'} = '';}
if (!defined($FORM{'query'})) {$FORM{'query'} = '';}
else {
&trim(\$FORM{'query'}); &z2h(\$FORM{'query'}); &tolower(\$FORM{'query'});
my @keyword = ();
foreach (split(/ /,$FORM{'query'})) {
if (@keyword >= 3) {last;}
push(@keyword,$_);
}
$FORM{'query'} = join(' ',@keyword);
}
if (!defined($FORM{&sessionid()})) {delete $FORM{'comment'}}
else {
$FORM{'comment'} = $FORM{&sessionid()};
&z2h(\$FORM{'comment'});
$FORM{'comment'} =~ s/ +$//mg;
$FORM{'comment'} =~ s/\n+$//;
}
if (!defined($FORM{'author'})) {$FORM{'author'} = '';} else {&trim(\$FORM{'author'}); &z2h(\$FORM{'author'});}
#クッキーの読み込み
&get_cookie(\%COOKIE);
if (!defined($COOKIE{'key'}) || ($COOKIE{'key'} !~ /^\w{10}$/)) {$COOKIE{'key'} = '';}
if (!defined($COOKIE{'author'})) {$COOKIE{'author'} = '';} else {&trim(\$FORM{'author'}); &z2h(\$FORM{'author'});}
#管理者チェック
if (defined($COOKIE{'admin'})) {
if ($COOKIE{'admin'} ne &sessionid_admin()) {$FORM{'admin'} = ''; &admin();}
if (time - (stat($DATADIR.'pass.lock'))[9] > 3600) {$FORM{'admin'} = ''; &admin();}
}
#セッションチェックと連続投稿チェック
if (($FORM{'mode'} eq 'insert') || ($FORM{'mode'} eq 'update')) {
my $timestamp = 0; if (-e $DATADIR.'9999.cgi') {$timestamp = (stat($DATADIR.'9999.cgi'))[9];}
if (($FORM{'mode'} eq 'insert') && ((my $wait = $timestamp - time + $POSTINTERVAL) > 0)) {&message('あと '.$wait.' 秒お待ちください。'); exit(0);}
if (($FORM{'mode'} eq 'update') && ((my $wait = $timestamp - time + 5) > 0)) {&message('あと '.$wait.' 秒お待ちください。'); exit(0);}
if (!defined($FORM{'comment'})) {&message('セッションが一致しません。トップページから投稿しなおしてください。'); exit(0);}
}
#各モードの呼び出し
if ($FORM{'mode'} eq 'view') {&view();}
elsif ($FORM{'mode'} eq 'input') {&input();}
elsif (($FORM{'mode'} eq 'insert') && ($ENV{'REQUEST_METHOD'} eq 'POST')) {&insert();}
elsif (($FORM{'mode'} eq 'update') && ($ENV{'REQUEST_METHOD'} eq 'POST')) {&update();}
elsif ($FORM{'mode'} eq 'remove') {&remove();}
elsif ($FORM{'mode'} eq 'search') {&search();}
elsif ($FORM{'mode'} eq 'admin') {&admin();}
$FORM{'mode'} = 'list'; &list();
exit(0);
}
####################
# モード "list"
####################
sub list {
#データの読み込み
my @data = ();
if (my $error = &load_data(\@data,$FORM{'page'})) {&message($error); exit(0);}
#ページの取得
my $page = '';
if ($FORM{'page'} ne '9999') {$page = '&page='.$FORM{'page'};}
###出力開始###
print 'Content-type: text/html; charset=Shift-JIS'."\n\n";
print &header();
#コントローラーの表示
print &controller();
#ツリーの表示
push(@data,'0000000000 0000000000 0000000000 0 0.0.0.0 AAAAAAAAAA '); #番犬
my ($thread,$root_old,$depth_old,$branch) = ('',0,0,1);
foreach my $index (0..$#data) {
if ($data[$index] !~ /^(\d{10})\t(\d{10})\t(\d{10})\t(\d+)\t(\d+\.\d+\.\d+\.\d+)\t(\w{10})\t(.*?)\t(.*?)$/) {
$thread .= 'Data error line '.($index + 1).'.
'."\n"; next;
}
my ($id,$parent,$root,$depth,$ip,$key,$comment,$author) = ($1,$2,$3,$4,$5,$6,$7,$8);
#スレッドの表示
if (!$root_old) {$root_old = $root;} elsif ($root != $root_old) {
print "\n$thread
\n\n";
$thread = ''; $root_old = $root; $depth_old = $depth; $branch = 1;
}
#タイトルとポップアップの取得
my ($subject,$popup) = ('','');
if ($comment =~ /
/) {$subject = $`; $popup = $';} else {$subject = $comment;}
&html_decode(\$subject); &trim(\$subject); &fold(\$subject,$SUBJECTMAX); &html_encode(\$subject);
if ($subject eq '') {$subject = '無題';}
if ($POPUPMAX) {
&html_decode(\$popup); $popup =~ s/^>.*?$//mg; &trim(\$popup); &fold(\$popup,$POPUPMAX); &html_encode(\$popup);
if (!$popup) {$popup = '(なし)';}
$popup = ' title="'.$popup.'"';
} else {$popup = '';}
#日時の取得
my $date = '';
{
my ($sec,$min,$hour,$day,$mon,$year) = (localtime($id))[0..5];
$date = sprintf("%04d/%02d/%02d",$year+1900,$mon+1,$day);
if (time - $id < 3600 * $DATE2) {$date = ' '.$date.'';}
elsif (time - $id < 3600 * $DATE1) {$date = ' '.$date.'';}
else {$date = ' '.$date.'';}
}
#投稿者の取得
if ($author) {$author = ' '.$author.'';}
#ブランクの取得
my $blank = '';
foreach (1..$depth) {$blank .= ' ';}
#子行のトグル
if ($depth <= $depth_old) {$branch = (!$branch);}
#1行追加
$depth_old = $depth;
if ($depth == 0) {
$thread .= '\n";
} else {
$thread .= '\n";
}
}
#文末の表示
print '
管理';
print &footer();
exit(0);
}
####################
# モード "view"
####################
sub view {
#データの読み込み
my @data = ();
if (my $error = &load_data(\@data,$FORM{'page'})) {&message($error); exit(0);}
#スレッドの取得
my @thread = ();
foreach (@data) {
if ($_ !~ /^(\d{10})\t(\d{10})\t(\d{10})\t(\d+)\t(\d+\.\d+\.\d+\.\d+)\t(\w{10})\t(.*?)\t(.*?)$/) {next;}
if ($3 == $FORM{'root'}) {push(@thread,$_);}
}
if (@thread == 0) {&message('記事 '.$FORM{'root'}.' が見つかりません。'); exit(0);}
###出力開始###
print 'Content-type: text/html; charset=Shift-JIS'."\n\n";
print &header();
print ''."\n";
foreach (@thread) {print &layout(split(/\t/,$_));}
print ' |
'."\n";
print ''."\n";
print &footer();
exit(0);
}
####################
# モード "input"
####################
sub input {
#入力フォームの取得
my ($action,$guide,$comment_value,$author_value) = ('','','',$COOKIE{'author'});
&html_encode(\$author_value);
if ($FORM{'parent'}) {
#返信の場合
my $parent_data = '';
{
my @data = (); if (my $error = &load_data(\@data,'9999')) {&message($error); exit(0);}
foreach (@data) {
if ($_ !~ /^(\d{10})\t(\d{10})\t(\d{10})\t(\d+)\t(\d+\.\d+\.\d+\.\d+)\t(\w{10})\t(.*?)\t(.*?)$/) {next;}
if ($1 == $FORM{'parent'}) {$parent_data = ''."\n".&layout($1,$2,$3,0,$5,$6,$7,$8).' |
'."\n"; last;}
}
}
if ($parent_data eq '') {&message('親記事 '.$FORM{'parent'}.' が見つかりません。'); exit(0);}
$guide .= $parent_data;
$guide .= '
上記へ返信します。新着一覧へ'."
\n";
$action .= ''."\n";
#編集ガイド
my $edit_guide = -1;
if ($EDITLIMIT && $FORM{'id'}) {$edit_guide = $FORM{'id'} - time + $EDITLIMIT * 60;}
elsif ($EDITLIMIT) {$edit_guide = $EDITLIMIT * 60;}
if ($edit_guide >= 0) {
if ($edit_guide >= 86400) {$edit_guide = int($edit_guide / 86400).'日';}
elsif ($edit_guide >= 3600) {$edit_guide = int($edit_guide / 3600).'時';}
elsif ($edit_guide > 0) {$edit_guide = int($edit_guide / 60).'分';}
else {$edit_guide = '0分';}
$edit_guide = '投稿内容は '.$edit_guide.'間以内 であれば修正できます。(あなたのPCからのみ)'."
\n";
} else {
$edit_guide = '';
}
###出力開始###
print 'Content-type: text/html; charset=Shift-JIS'."\n\n";
print &header();
print $guide;
print ''."
\n";
print '先頭行はタイトルとしても扱われます。'."
\n";
print '全角の英数字は、投稿時に半角へ自動変換されます。'."
\n";
print $edit_guide;
print ''."\n";
print &footer();
exit(0);
}
####################
# モード "insert"
####################
sub insert {
#入力チェック
{
my $error = '';
if ($FORM{'comment'} eq '') {$error .= 'コメントが空です。
';}
if ((my $over = (length($FORM{'comment'}) - $COMMENTMAX)) > 0) {$error .= 'コメントが '.$over.' バイト多すぎます。
';}
{
my $over = 1;
while ($FORM{'comment'} =~ /\n/g) {$over++;}
$over -= $COMMENTLINEMAX;
if ($over > 0) {$error .= 'コメントが '.$over.' 行多すぎます。
';}
}
if ((my $over = (length($FORM{'author'}) - $AUTHORMAX)) > 0) {$error .= '名前が '.$over.' バイト多すぎます。
';}
if ($error) {&message($error); exit(0);}
}
#クッキーの準備
if ($COOKIE{'key'} eq '') {
my @code = ('0'..'9','A'..'Z','a'..'z');
for (my $i = 10;$i > 0;$i--) {$COOKIE{'key'} .= $code[int(rand(62))];}
}
$COOKIE{'author'} = $FORM{'author'};
#サニタイズ
&html_encodex(\$FORM{'comment'});
&html_encode(\$FORM{'author'});
#URLの自動リンク
{
my $cgiurl = $CGIURL;
if ($CGIURL !~ /^http:/i) {$cgiurl = ("http://$ENV{'HTTP_HOST'}$ENV{'REQUEST_URI'}" =~ /^(.+?\.cgi)/i)[0];}
$FORM{'comment'} =~ s/$cgiurl/http:\/\/CGIURL/ig;
$FORM{'comment'} =~ s/(http:\/\/[\w\#\%\&\+\-\.\/\:\;\=\?\@\~]+)/$1<\/a>/ig;
}
### Lock ###
if (my $error = &lock()) {&message($error); exit(0);}
#データの読み込み
my @data = ();
if (my $error = &load_data(\@data,'9999')) {&message($error); &unlock(); exit(0);}
#$id,$parent,$root,$depthの取得
my ($id,$parent,$root,$depth) = (time,0,0,0);
if ($FORM{'parent'}) {
foreach (@data) {
if ($_ !~ /^(\d{10})\t(\d{10})\t(\d{10})\t(\d+)\t(\d+\.\d+\.\d+\.\d+)\t(\w{10})\t(.*?)\t(.*?)$/) {next;}
if ($1 == $FORM{'parent'}) {($parent,$root,$depth) = ($1,$3,$4); ++$depth; last;}
}
}
if (!$depth) {$parent = $root = $id;}
#データの追加
my $line = sprintf("%10d\t%10d\t%10d\t%d",$id,$parent,$root,$depth)."\t".$ENV{'REMOTE_ADDR'}."\t".$COOKIE{'key'}."\t".$FORM{'comment'}."\t".$FORM{'author'};
if (!$depth) {
unshift(@data,$line);
} else {
#挿入位置の取得
my ($top,$here,$last,$flag) = (-1,-1,-1,0);
push(@data,'0000000000 0000000000 0000000000 0 0.0.0.0 AAAAAAAAAA '); #番犬
foreach (@data) {
$last++;
if ($_ !~ /^(\d{10})\t(\d{10})\t(\d{10})\t(\d+)\t(\d+\.\d+\.\d+\.\d+)\t(\w{10})\t(.*?)\t(.*?)$/) {
if ($top < 0) {next;}
else {&message('このスレッドはデータが壊れている可能性があるため返信できません。'); &unlock(); exit(0);}
}
if ($3 != $root) {if ($top < 0) {next;} last;}
if ($top < 0) {$top = $last; next;}
if ($here < 0) {if ($flag) {if ($4 < $depth) {$here = $last;}} elsif ($1 == $parent) {$flag++;} next;}
}
if ($here < 0) {$here = $last;}
pop(@data);
#1行挿入
splice(@data,$here,0,$line);
#スレッドの浮上
if ($UPTHREAD) {
my @thread = splice(@data,$top,$last-$top+1);
unshift(@data,@thread);
}
}
#データの書き込み
if (my $error = &save_data(\@data)) {&message($error); &unlock(); exit(0);}
###出力開始###
print 'Content-type: text/html; charset=Shift-JIS'."\n";
print &set_cookie(\%COOKIE)."\n";
print &header();
print '投稿ありがとうございました。新着一覧へ'."
\n";
print ''."\n".&layout($id,$parent,$root,0,$ENV{'REMOTE_ADDR'},$COOKIE{'key'},$FORM{'comment'},$FORM{'author'}).' |
'."\n";
print &footer();
&unlock();
exit(0);
}
####################
# モード "update"
####################
sub update {
#入力チェック
if (!$COOKIE{'admin'} && ($FORM{'id'} + $EDITLIMIT * 60 - time < 0)) {&message('この記事は修正できません。'); exit(0);}
{
my $error = '';
if ($FORM{'comment'} eq '') {$error .= 'コメントが空です。
';}
if ((my $over = (length($FORM{'comment'}) - $COMMENTMAX)) > 0) {$error .= 'コメントが '.$over.' バイト多すぎます。
';}
{
my $over = 1;
while ($FORM{'comment'} =~ /\n/g) {$over++;}
$over -= $COMMENTLINEMAX;
if ($over > 0) {$error .= 'コメントが '.$over.' 行多すぎます。
';}
}
if ((my $over = (length($FORM{'author'}) - $AUTHORMAX)) > 0) {$error .= '名前が '.$over.' バイト多すぎます。
';}
if ($error) {&message($error); exit(0);}
}
#サニタイズ
&html_encodex(\$FORM{'comment'});
&html_encode(\$FORM{'author'});
#URLの自動リンク
{
my $cgiurl = $CGIURL;
if ($CGIURL !~ /^http:/i) {$cgiurl = ("http://$ENV{'HTTP_HOST'}$ENV{'REQUEST_URI'}" =~ /^(.+?\.cgi)/i)[0];}
$FORM{'comment'} =~ s/$cgiurl/http:\/\/CGIURL/ig;
$FORM{'comment'} =~ s/(http:\/\/[\w\#\%\&\+\-\.\/\:\;\=\?\@\~]+)/$1<\/a>/ig;
}
### Lock ###
if (my $error = &lock()) {&message($error); exit(0);}
#データの読み込み
my @data = ();
if (my $error = &load_data(\@data,'9999')) {&message($error); &unlock(); exit(0);}
#データの更新
my ($parent,$root,$depth,$key) = (0,0,0,'');
{
my $offset = -1;
foreach (@data) {
++$offset;
if ($_ !~ /^(\d{10})\t(\d{10})\t(\d{10})\t(\d+)\t(\d+\.\d+\.\d+\.\d+)\t(\w{10})\t(.*?)\t(.*?)$/) {next;}
if ($1 == $FORM{'id'}) {($parent,$root,$depth,$key) = ($2,$3,$4,$6); last;}
}
if (!$parent || !$root) {&message('記事 '.$FORM{'id'}.' が見つかりません。'); &unlock(); exit(0);}
if (!$COOKIE{'admin'} && ($key ne $COOKIE{'key'})) {&message('クッキーが一致しません。'); &unlock(); exit(0);}
my $line = sprintf("%10d\t%10d\t%10d\t%d",$FORM{'id'},$parent,$root,$depth)."\t".$ENV{'REMOTE_ADDR'}."\t".$key."\t".$FORM{'comment'}."\t".$FORM{'author'};
splice(@data,$offset,1,$line);
}
#データの書き込み
if (my $error = &save_data(\@data)) {&message($error); &unlock(); exit(0);}
###出力開始###
print 'Content-type: text/html; charset=Shift-JIS'."\n\n";
print &header();
print '修正しました。新着一覧へ'."
\n";
print ''."\n".&layout($FORM{'id'},$parent,$root,0,$ENV{'REMOTE_ADDR'},$key,$FORM{'comment'},$FORM{'author'}).' |
'."\n";
print &footer();
&unlock();
exit(0);
}
####################
# モード "remove"
####################
sub remove {
if (!$COOKIE{'admin'}) {&message('削除できるのは管理者のみです。'); exit(0);}
### Lock ###
if (my $error = &lock()) {&message($error); exit(0);}
#データの読み込み
my @data = ();
if (my $error = &load_data(\@data,'9999')) {&message($error); &unlock(); exit(0);}
#データの削除
{
my $offset = -1;
foreach (@data) {
++$offset;
if ($_ !~ /^(\d{10})\t(\d{10})\t(\d{10})\t(\d+)\t(\d+\.\d+\.\d+\.\d+)\t(\w{10})\t(.*?)\t(.*?)$/) {next;}
if ($1 == $FORM{'id'}) {splice(@data,$offset,1); $offset = -1; last;}
}
if ($offset >= 0) {&message('記事 '.$FORM{'id'}.' が見つかりません。'); &unlock(); exit(0);}
}
#データの書き込み
if (my $error = &save_data(\@data)) {&message($error); &unlock(); exit(0);}
###出力開始###
print 'Content-type: text/html; charset=Shift-JIS'."\n\n";
print &header();
print '削除しました。新着一覧へ'."
\n";
print &footer();
&unlock();
exit(0);
}
####################
# モード "search"
####################
sub search {
#キーワード
my @keyword = split(/\s/,$FORM{'query'});
if (@keyword == 0) {&message('キーワードを入力してください。'); exit(0);}
#キーワード(HTMLエンコード版)
my @keyword_html = @keyword;
foreach (@keyword_html) {&html_encode(\$_);}
#キーワード(URLエンコード版)
my $keyword_url = $FORM{'query'};
$keyword_url =~ s/(\W)/sprintf("%%%02X",ord($1))/eg; $keyword_url =~ s/%20/\+/g;
#データの読み込み
my @data = ();
if (my $error = &load_data(\@data,$FORM{'page'})) {&message($error); exit(0);}
#検索結果の取得
my ($result,$buf,$subject,$root_old,$thread_count,$hit_count,$comment_count) = ('','','',0,0,0,0);
push(@data,'0000000000 0000000000 0000000000 0 0.0.0.0 AAAAAAAAAA '); #番犬
foreach (@data) {
my ($root,$comment,$author) = (0,'','');
if ($_ =~ /^(\d{10})\t(\d{10})\t(\d{10})\t(\d+)\t(\d+\.\d+\.\d+\.\d+)\t(\w{10})\t(.*?)\t(.*?)$/) {($root,$comment,$author) = ($3,$7,$8);}
if (!$root_old) {$root_old = $root;} elsif ($root != $root_old) {
$thread_count++;
#小文字化
&tolower(\$buf);
#ヒット判定
HITCHECK:{
#簡易ヒット判定
foreach (@keyword_html) {
if (index($buf,$_) < 0) {last HITCHECK;}
}
#タイトルの取得
&html_decode(\$subject); &trim(\$subject); &fold(\$subject,$SUBJECTMAX); &html_encode(\$subject);
if ($subject eq '') {$subject = '無題';}
#日時の取得
my $date = '';
{
my ($sec,$min,$hour,$day,$mon,$year) = (localtime($root_old))[0..5];
$date = sprintf("%04d/%02d/%02d",$year+1900,$mon+1,$day);
}
#検索結果概要の取得
my %hit = (); my $all_hit = 0;
my $summary = '';
SUMMARYLOOP:foreach my $line (split(/
/,$buf)) {
&html_decode(\$line); &trim(\$line);
my $hit_flag = 0;
foreach (@keyword) {
if (my $hit_count = &replace(\$line,$_,chr(0x0e).$_.chr(0x0f))) {
$hit_flag += $hit_count;
$hit{$_} += $hit_count;
}
}
if (!$hit_flag) {next SUMMARYLOOP}
if (length($summary) < $SUMMARYMAX) {$summary .= $line.' ';}
$all_hit = 1; foreach (@keyword) {if (!defined($hit{$_})) {$all_hit = 0;}}
if ($all_hit && length($summary) >= $SUMMARYMAX) {last SUMMARYLOOP;}
}
#全キーワードがヒットしたら表示
if ($all_hit) {
$hit_count++;
&html_encode(\$summary);
$summary =~ s/\x0e//g;
$summary =~ s/\x0f/<\/span>/g;
$result .= '\n";
}
}
#次処理
$root_old = $root; $buf = $subject = ''; $comment_count = 0;
}
$buf .= $author.' '.$comment.'
';
if ($subject eq '') {if ($comment =~ /
/) {$subject = $`;} else {$subject = $comment;}}
$comment_count++;
}
###出力開始###
print 'Content-type: text/html; charset=Shift-JIS'."\n\n";
print &header();
#コントローラーの表示
print &controller();
#検索結果の表示
print '指定したページに '.join(' ',@keyword_html).' を含むスレッドが '.$hit_count.'件 見つかりました。('.$thread_count.'件中)'."
\n";
print $result;
print '検索モードを解除するには"検索解除"ボタンをクリックしてください。'."
\n";
print 'キーワードは3つまで指定できます。各キーワードはスペース(全角スペースも可)で区切ります。'."
\n";
print &footer();
exit(0);
}
####################
# モード "admin"
####################
sub admin {
my ($logcount,$logsize) = (0,0);
if (!opendir(DIR,$DATADIR)) {return($!.' '.$DATADIR);}
foreach (readdir(DIR)) {if ($_ =~ /^\d{4}\.cgi$/) {++$logcount; $logsize += (stat($DATADIR.$_))[7];}}
closedir(DIR);
if (!defined($FORM{'admin'})) {
print 'Content-type: text/html; charset=Shift-JIS'."\n\n";
print &header();
print ''."\n";
print '管理者パスワードを入力してください。新着一覧へ'."
\n";
print ''."\n";
print 'ログファイル数: '.$logcount."
\n";
print 'ログサイズ: '.int($logsize / 1024)."KB
\n";
print 'PureTree 1.08'."
\n";
print ''."\n";
print &footer();
} elsif ($FORM{'admin'} eq $PASS) {
if (my $error = &passlock()) {&message($error); exit(0);}
$COOKIE{'admin'} = &sessionid_admin();
print 'Content-type: text/html; charset=Shift-JIS'."\n";
print &set_cookie(\%COOKIE)."\n";
print &header();
print 'あなたのPCは管理者モードに設定されました。記事の修正と削除が可能です。新着一覧へ'."
\n";
print '管理者モードは 1時間後 に自動解除されます。'."
\n";
print '空パスワードの送信で管理者モードを手動で解除することもできます。'."
\n";
print &footer();
} else {
if (my $error = &passlock()) {&message($error); exit(0);}
my $message = 'パスワードが違います。';
if (defined($COOKIE{'admin'})) {$message = '管理者モードは解除されました。';}
delete($COOKIE{'admin'});
print 'Content-type: text/html; charset=Shift-JIS'."\n";
print &set_cookie(\%COOKIE)."\n";
print &header();
print $message.'新着一覧へ'."
\n";
print &footer();
}
exit(0);
}
####################
# 操作部のレイアウト(検索ボタン、新規投稿ボタンなど)
# In: none
# Out: $str
####################
sub controller {
#ページリストの取得
my $pagelist = '';
{
my @filelist = ();
if (!opendir(DIR,$DATADIR)) {return($!.' '.$DATADIR);}
foreach (reverse sort readdir(DIR)) {if ($_ =~ /^(\d{4})\.cgi$/) {push(@filelist,$1);}}
closedir(DIR);
if (@filelist > 1) {
my $query = '';
if ($FORM{'query'} ne '') {
$query = $FORM{'query'};
$query =~ s/(\W)/sprintf("%%%02X",ord($1))/eg; $query =~ s/%20/\+/g;
$query = '&query='.$query;
}
my $position = 0;
foreach (@filelist) {if ($_ eq $FORM{'page'}) {last;} ++$position;}
my $position_s = int($position / $PAGEMAX) * $PAGEMAX;
my $position_e = $position_s + $PAGEMAX - 1; if ($position_e > @filelist - 1) {$position_e = @filelist - 1;}
if ($position_s > 0) {$pagelist .= ' <<';}
foreach ($position_s .. $position_e) {
my $disp = $filelist[$_]; if ($disp eq '9999') {$disp = '新着';}
if ($filelist[$_] eq $FORM{'page'}) {$disp = ''.$disp.'';}
$pagelist .= ' '.$disp.'';
}
if ($position_e < @filelist - 1) {$pagelist .= ' >>';}
}
}
#キーワードの取得
my $query_html = $FORM{'query'};
&html_encode(\$query_html);
#操作部のレイアウト
my $str = '';
$str .= ''."\n";
if ($pagelist) {$str .= ''.$pagelist.' | '."\n";}
if ($FORM{'mode'} eq 'search') {
$str .= ' | '."\n";
} else {
$str .= ' | '."\n";
}
$str .= '
'."\n\n";
return($str);
}
####################
# 記事のレイアウト
# In: $id,$parent,$root,$depth,$ip,$key,$comment,$author
# Out: $str
####################
sub layout {
my ($id,$parent,$root,$depth,$ip,$key,$comment,$author) = @_;
if (!defined($comment)) {$comment = '';}
if (!defined($author)) {$author = '';}
if ($CGIURL =~ /^http:/i) {$comment =~ s/http:\/\/CGIURL/$CGIURL/g;}
else {my $cgiurl = ("http://$ENV{'HTTP_HOST'}$ENV{'REQUEST_URI'}" =~ /^(.+?\.cgi)/i)[0]; $comment =~ s/http:\/\/CGIURL/$cgiurl/g;}
#キーワードのハイライト
if ($FORM{'query'} ne '') {
my @tag = (); $comment =~ s/(<.*?>)/push(@tag,$1);"\a";/eg; #タグの退避
&html_decode(\$comment); &html_decode(\$author);
{
#キーワードを0Eと0Fで囲む
my ($comment_pure,$author_pure) = ($comment,$author);
&tolower(\$comment_pure); &tolower(\$author_pure);
my @comment_buf = split(/\a/,$comment_pure);
foreach my $keyword (split(/\s/,$FORM{'query'})) {
foreach (@comment_buf) {&replace(\$_,$keyword,chr(0x0e).$keyword.chr(0x0f));}
&replace(\$author_pure,$keyword,chr(0x0e).$keyword.chr(0x0f));
}
$comment_pure = join("\a",@comment_buf);
my ($offset,$length) = (0,0);
$comment_pure =~ s/(.*?)(\x0e|\x0f|$)/$length = length($1); $offset += length($1); substr($comment,$offset - $length,$length).$2;/eg;
($offset,$length) = (0,0);
$author_pure =~ s/(.*?)(\x0e|\x0f|$)/$length = length($1); $offset += length($1); substr($author,$offset - $length,$length).$2;/eg;
$comment = $comment_pure;
$author = $author_pure;
}
&html_encodex(\$comment); &html_encode(\$author);
$comment =~ s/\x0e//g; $comment =~ s/\x0f/<\/span>/g;
$author =~ s/\x0e//g; $author =~ s/\x0f/<\/span>/g;
$comment =~ s/\a/shift(@tag);/eg; #タグの復活
}
#日時の取得
my $date = '';
{
my ($sec,$min,$hour,$day,$mon,$year) = (localtime($id))[0..5];
$date = sprintf("%04d/%02d/%02d %02d:%02d",$year+1900,$mon+1,$day,$hour,$min);
if (time - $id < 3600 * $DATE2) {$date = ''.$date.'';}
elsif (time - $id < 3600 * $DATE1) {$date = ''.$date.'';}
else {$date = ''.$date.'';}
}
#投稿者の取得
if ($author) {$author = ' '.$author.'';}
#先頭行(操作リンク)の作成
my $control = '';
if ($FORM{'page'} eq '9999') {
#日時+投稿者+削除修正返信
$control .= '';
if ($COOKIE{'admin'}) {
$control .= '
削除 ';
}
if (($COOKIE{'admin'}) || (($key eq $COOKIE{'key'}) && ($id + $EDITLIMIT * 60 - time > 0))) {
$control .= '
修正 ';
}
$control .= '
返信';
$control .= '
';
$control .= $date.$author.'
';
}
else {
#日時+投稿者
$control .= $date.$author.'
';
}
#レイアウト
my $str = '';
$str .= '';
$str .= '';
$str .= $control;
$str .= '';
$str .= '
';
$str .= "\n";
return($str);
}
##################################################
#################### 文字列系 ####################
##################################################
####################
# Header Contents
# In: $subject
# Out: $header
####################
sub header {
my $str = '';
$str .= ''."\n";
$str .= ''."\n";
$str .= ''."\n";
$str .= ' '."\n";
$str .= ' '."\n";
$str .= ' '."\n";
$str .= ' '."\n";
$str .= ' '."\n";
$str .= ' '.$TITLE.''."\n";
$str .= ''."\n";
$str .= ''."\n";
$str .= $TOPLAYOUT."\n";
return($str);
}
####################
# Footer Contents
# In: none
# Out: $footer
####################
sub footer {
my $str = '';
$str .= ''."\n";
$str .= ''."\n";
return($str);
}
####################
# Encode HTML
# In: \$str
# Out: none
####################
sub html_encode {
my ($str) = @_;
if (!$$str) {return(0);}
$$str =~ s/&/&/g;
$$str =~ s/</g;
$$str =~ s/>/>/g;
$$str =~ s/"/"/g;
return(0);
}
####################
# Encode HTML for Textarea
# In: \$str
# Out: none
####################
sub html_encodex {
my ($str) = @_;
if (!$$str) {return(0);}
$$str =~ s/&/&/g;
$$str =~ s/</g;
$$str =~ s/>/>/g;
$$str =~ s/"/"/g;
$$str =~ s/^ / /mg;
$$str =~ s/ / /g;
$$str =~ s/\n/
/g;
return(0);
}
####################
# Decode HTML
# In: \$str
# Out: 0
####################
sub html_decode {
my ($str) = @_;
if (!$$str) {return(0);}
$$str =~ s/
/\n/g;
$$str =~ s/<.*?>//g;
$$str =~ s/ / /g;
$$str =~ s/"/"/g;
$$str =~ s/>/>/g;
$$str =~ s/</';
print @_;
print '
新着一覧へ