= "4.1.0") { extract($_GET); extract($_POST); extract($_SERVER); extract($_COOKIE); } $PHP_SELF = $SCRIPT_NAME; /************************************* * Tree BBS by ToR * * http://php.s3.to/ * ************************************* * ツリー掲示板です。 * ・いろんな表示モードが選べます * ・クッキーによりデフォルトの表示モードと件数が設定できます * ・検索機能があります * ・二重投稿・連続投稿防止機能があります * ・禁止語句を**に変換します * ・許可タグを指定できます * ・新着記事にはNew等の文字を表示できます * ・ツリーの深さを制限できます * ・好きなデザインにしやすいです * * 準備 * ・空のログファイルを用意して書き込み属性を与えて下さい * ・過去ログを使う場合は過去ログディレクトリを書き込み可にして、 *  past.txtを用意して中に1とだけ書いて保存し、書き込み可にして下さい。 * * 6/17 過去ログ修正、検索可 * 04/2/22 レス数制限、script除去追加 * 04/08/14 Line943: /<($tags)(.*?)>/si → /<($tags)\b(.*?)>/si * 09/06/22 XSS脆弱性修正 */ class tree{ var $logarr = array(); var $id = array(); var $thread = array(); var $root = array(); var $tree_arr = array(); var $error = ""; /* 設定ここから */ // ツリー記号 var $i_gif = "│"; var $t_gif = "├"; var $l_gif = "└"; var $nogif = " "; // ログファイル名 var $logfile = "log.log"; // ログ最大行 var $max_log = 100; // 過去ログ機能を使う?YES=1,No=0 var $past = 0; // 過去ログNoファイル(初期値は1)(1.log 2.logを作成 var $pastno = "past.txt"; // 過去ログ保存ディレクトリ(書き込み可能にする。707等 var $pastdir = "./dat/"; // コメント最大行 var $max_line = 25; // 投稿フォーム表示から投稿までの制限秒 var $posttime = 3; // 連続投稿制限秒 var $renzoku = 10; // 最新?時間内の投稿にNew記号 var $expire = 24; // ツリーの深さ制限 var $max_depth = 20; // ツリーのレス数制限 var $max_res = 50; // ツリー時の一行表示文字数(記号+題名+文字を半角で) var $cut_size = 60; // 許可するタグ(本文のみ) var $allow_tag = "

"; // 禁止する文字(**に変換) var $bad_word = "href=|url=|バカ|死ね|氏ね|架空口座|販売|精力|媚薬|中絶|http://"; // >引用がついた時の色 var $quote = "#aaaacc"; // 投稿があると管理人にメールで通知する場合のアドレス(不要なら空) var $adm_mail = ""; // 管理人に来るメールのsubject({id},{subj}使用可) var $adm_subj = "[BBS-{id} {subj} ]"; // 管理人パスワード。単独記事表示の削除フォームから var $adm_pass = "pass1212"; // 初期表示モード(tree=ツリー,expn=スレッド,root=タイトル,dump=一覧 var $mode = "root"; // 表示件数の初期状態 var $page_tree = 10; var $page_expn = 5; var $page_root = 15; var $page_dump = 20; // クッキー保存名 var $cookie_n = "treebbs_cookie"; var $cookie_s = "treebbs_setting"; // ヘッダ部分 var $head = ' サポート掲示板

サポート掲示板


今日:{today} 昨日:{yesterday} 合計:{total}
'; /** 変数説明 * {id}-記事No {subj}-題名 {name}-投稿者 {date}-投稿日 {com}-本文 {host}-ホスト {time}-投稿time * {n}-単独記事表示リンク {all}-ツリー一覧表示リンク {reply}-返信リンク {nl}で\n(''で囲った時用) * {new}で新着$new_msg表示 {email}で以下の$if_email文字にリンク {url}で以下の$if_url文字にリンク */ // タイトル表示ヘッダ var $root_head = "
\n"; // タイトル表示本文 {lname}最新投稿者 {ldate}最新投稿日 {lcom} 最新本文 var $root_msg = "\n"; // タイトル表示フッタ var $root_foot = "
No.タイトル投稿者レス最終更新
{num}{subj}{name}{res}{ldate}
by {lname} {host}

\n"; // ツリー表示ヘッダ var $tree_head = "
"; // ツリー表示親記事 var $tree_oya = "
-{subj} [{name}] ({date})"; // ツリー表示子記事 var $tree_res = "{subj} [{name}] ({date}) {new}"; // ツリー表示フッタ var $tree_foot = "

\n"; // 展開表示親記事 var $expn_oya = "
{num}: {subj}
Name: {name} {url} {email}
Date: {date}
{com}
    \n"; // 展開表示子記事 var $expn_res = "
    [{id}] {subj}
    Name: {name} {url} {email}
    Date: {date}
    {com}
    \n"; // 展開表示テーブル閉じ var $expn_bott = "

\n"; // 一覧・単独表示 var $dump_msg = "
[{id}] {subj}
Name: {name} {url} {email}
Date: {date}
{com}

返信する

\n"; //単独表示時関連ツリー開始・終了タグ var $n_tree_st = "

関連ツリー\n"; var $n_tree_to = "

\n"; //ツリー一括時ツリー開始・終了タグ var $all_tree_st = "
No.{id}に関するツリー"; var $all_tree_to = "
\n"; // フッタ部分 var $foot = "\n
"; // 新着記事記号 var $new_msg = "New!"; // E-Mailがあるとき{email}のリンク文字(空なら名前にリンク) var $if_email = "E-MAIL"; // URLがあるとき{url}のリンク文字 var $if_url = "(HOME)"; /*** * コンストラクタ * */ function tree($mode, $log){ global ${$this->cookie_s}; // $this->mode = "tree"; if(${$this->cookie_s}){ list($view,$pagetree,$pageexpn,$pageroot,$pagedump)=explode(",",${$this->cookie_s}); $this->mode = $view; $this->page_tree = $pagetree; $this->page_expn = $pageexpn; $this->page_root = $pageroot; $this->page_dump = $pagedump; } if($mode) $this->mode = $mode; if($log && file_exists($this->pastdir.$log.".log")) { $this->logfile = $this->pastdir.$log.".log"; } $this->logarr = file($this->logfile); } /*** * なびめにゅう表示 * */ function show_navi(){ global $PHP_SELF,$all,$n,$log; if($log) $kako = "&log=$log"; $form = ($this->mode == "form" && !$all && !$n) ? "新規投稿" : "
新規投稿"; $tree = ($this->mode == "tree" && !$all && !$n) ? "ツリー" : "ツリー"; $expn = ($this->mode == "expn" && !$all && !$n) ? "スレッド" : "スレッド"; $root = ($this->mode == "root" && !$all && !$n) ? "タイトル" : "タイトル"; $dump = ($this->mode == "dump" && !$all && !$n) ? "一覧" : "一覧"; $search = ($this->mode == "search") ? "検索" : "検索"; if ($this->past) $past_m = "ログ | "; $setup = ($this->mode == "setup") ? "設定" : "設定"; $html = "
 {$form} | {$tree} | {$expn} | {$root} | {$dump} | {$search} | {$past_m}{$setup} 

\n"; return $html; } /*** * 改ページ表示 * */ function show_jump($offset, $limit, $total){ global $PHP_SELF,$log; if($log) $kako = "&log=$log"; $num_page = ceil($total/$limit); $prev = $offset - 1; $next = $offset + 1; if($prev >= 1){ $prev_st = ""; $prev_to = ""; } if($next <= $num_page){ $next_st = ""; $next_to =""; } $html.= "
[ {$prev_st}前の {$limit} 件{$prev_to} | {$offset} /{$num_page}ページ | {$next_st}次の {$limit} 件{$next_to} ]
\n"; return $html; } /*** * ヘッドライン表示 * */ function show_headline($offset,$limit){ global $PHP_SELF,$log; if($log) $kako = "&log=$log"; $st = ($offset-1) * $limit; if($st < 0) $st = 0; $html = "
"; for($i = $st; $i < $st+$limit*5; $i++){ $id = $this->root[$i]; if(!$id) continue; $resc = 0; foreach($this->thread[$id] as $kidno){ $resc += count($kidno); } list(,,,$subj,) = explode("\t", $this->id[$id]); $num = $i+1; if($i < $st+$limit) $html.= "".$num.": ".$subj."(".$resc.") \n"; else $html.= "".$num.": ".$subj."(".count($this->thread[$id]).") "; } $html.= "

\n"; return $html; } /*** * 投稿フォーム表示 * */ function show_form($root=0, $oya=0, $subj='', $com='', $mode='quote', $name='', $email='', $url='', $no='', $pass=''){ global $PHP_SELF,$n,${$this->cookie_n},$log; $sid = session_id(); $_SESSION['time'] = time(); if ($mode == "quote") { if(get_magic_quotes_gpc()) ${$this->cookie_n} = stripslashes(${$this->cookie_n}); if(ereg("Re\[([0-9]+)\]:", $subj, $reg)){ $reg[1]++; $r_sub=ereg_replace("Re\[([0-9]+)\]:", "Re[$reg[1]]:", $subj); }elseif(ereg("^Re:", $subj)){ $r_sub=ereg_replace("^Re:", "Re[2]:", $subj); }elseif($subj){ $r_sub = "Re:$subj"; } if($com){ $i_com = ">$com"; $i_com = str_replace("'", "\'", $i_com); $i_com = str_replace(">", ">", $i_com); $i_com = str_replace("<", "<", $i_com); $i_com = eregi_replace("", "
", $i_com); } list($c_name,$c_email,$c_url,$c_pass) = explode(",", ${$this->cookie_n}); $hidden = ""; } if ($mode == "edit") { $r_sub = $subj; $r_com = $com; $c_name = $name; $c_email = $email; $c_url = $url; $c_pass = $pass; $hidden = ""; } if(isset($log)) $kako = ""; if($this->allow_tag) $tag = "タグ使用可 ".htmlspecialchars($this->allow_tag); $html.= ' '; $html.= "
$hidden
 "; if($mode == "quote") $html.= ($root) ? "返信フォーム [引用]" : "新規投稿フォーム"; if($mode == "edit") $html.= "編集フォーム"; $html.= "

 名前 *
 E-Mail
 題名 *
 URL
 Pass $tag
レスが付けば上記のEmailアドレスにメールで知らせる

"; return $html; } /*** * 検索フォーム表示 * */ function show_search($word){ global $PHP_SELF,$log; if(get_magic_quotes_gpc()) $word = stripslashes($word); $word = htmlspecialchars($word); if (is_numeric($log)) { $num = sprintf("%03d",$log); $kako = "過去ログ $num から"; $hide = ""; } $html = "
[ {$kako}記事検索 ]

{$hide}


"; return $html; } /*** * 設定フォーム表示 * */ function show_setup(){ global $PHP_SELF,${$this->cookie_s}; list($view,) = explode(",", ${$this->cookie_s}); $$view = " selected"; if(!$view) $tree = " selected"; $html = "
[ 環境設定 ]

デフォルトの表示モード

ツリー表示時の1ページの表示数 page_tree>
展開表示時の1ページの表示数 page_expn>
タイトル表示時の1ページの表示数 page_root>
一覧表示時の1ページの表示数 page_dump>



"; return $html; } /*** * 過去ログフォーム * */ function show_past(){ global $PHP_SELF; $html = "
[ ログ選択 ]
古い順に並んでいます

"; $no = file($this->pastno); for($i=1; $i<=$no[0]; $i++){ $num = sprintf("%03d",$i); if(file_exists($this->pastdir.$i.".log")){ $html.="過去ログ $num
"; } } $html.="現在のログ



"; return $html; } /*** * 削除フォーム * */ function show_del($n){ global $PHP_SELF,${$this->cookie_n}; list(,,,$pass) = explode(",", ${$this->cookie_n}); $html = "
Pass
"; return $html; } /*** * ログを配列に * */ function get_arr(){ for($i = 0; $i < count($this->logarr); $i++){ //記事番号\t親記事番号\t根記事番号 list($id, $oya, $root,) = explode("\t", $this->logarr[$i]); //$this->id : 記事番号をキーにしたログデータ配列 $this->id[$id] = $this->logarr[$i]; //$this->thread : スレッド構造の配列。値は記事番号 $this->thread[$root][$oya][] = $id; //$this->root : 根記事のみの配列。ログ順 if($oya==0) $this->root[] = $id; } //print_r($this->thread); } /*** * メインHTML生成処理 * */ function show_main($page){ if(intval($page) == 0) $page = 1;//最初はページ1 //ヘッダ部分 switch($this->mode){ //タイトルモード case 'root': //ページング生成 $jump = tree::show_jump($page, $this->page_root, count($this->root)); //ヘッダHTML $html = $jump . $this->root_head; //1ページ表示数決定 $page_def = $this->page_root; break; //展開モード case 'expn': $jump = tree::show_jump($page, $this->page_expn, count($this->root)); //ヘッドライン $html = $jump . tree::show_headline($page, $this->page_expn); $page_def = $this->page_expn; break; //ツリーモード case 'tree': $jump = tree::show_jump($page, $this->page_tree, count($this->root)); $html = $jump . $this->tree_head; $page_def = $this->page_tree; break; //一覧モード case 'dump': $jump = tree::show_jump($page, $this->page_dump, count($this->id)); $html = $jump; //最新番号取得 list($st,) = explode("\t", $this->logarr[0]); //データ開始位置 $st = ($page) ? $st - ($page-1) * $this->page_dump : $st; //最新記事順に表示 for($i = $st; $i > $st-$this->page_dump; $i--){ $html.= tree::show_msg($i, $this->mode); } //フッタ表示で終了 $html.= "
" . $jump; return $html; break; } //データ開始位置 $st = ($page) ? ($page-1) * $page_def : 0; //親記事番号でループ for($i = $st; $i < $st+$page_def; $i++){ if($this->root[$i]=="") continue; switch($this->mode){ //タイトルモード case 'root': $html.= tree::show_msg($this->root[$i], 'root'); $html = str_replace('{num}', $i+1, $html); break; //展開モード case 'expn': //ツリー構造を初期化 $this->tree_arr = array(); //ツリー生成 tree::make_tree($this->root[$i]); //ツリー順に表示 foreach($this->tree_arr as $val){ $html.= tree::show_msg($val[0], 'expn'); $html = str_replace('{num}', $i+1, $html); } //テーブル閉じ $html.= $this->expn_bott; break; //ツリーモード case 'tree': $this->tree_arr = array(); tree::make_tree($this->root[$i]); foreach($this->tree_arr as $val){ $html.= tree::show_msg($val[0], 'tree', $val[1]); } break; } } //フッタ部分 switch($this->mode){ case 'root': $html.= $this->root_foot; break; case 'tree': $html.= $this->tree_foot; break; } //ページング表示 $html.= $jump; return $html; } /*** * ツリーの生成、並び替え * */ function make_tree($id, $oya=0, $pre="", $img=""){ //前ツリーがLならその下は空 if($img == $this->l_gif) $pre.= $this->nogif; //前ツリーがTならその下はI if($img == $this->t_gif) $pre.= $this->i_gif; //ツリー構造なら if(is_array($this->thread[$id][$oya])){ foreach($this->thread[$id][$oya] as $no){ //下ツリーがあればT記号終わってればL記号 $img = (count($this->thread[$id][$oya])>1) ? $this->t_gif : $this->l_gif; //子記事は後ろに送る $no = ($oya==0) ? array_shift($this->thread[$id][$oya]) : array_pop($this->thread[$id][$oya]); //ツリー構造と記号を配列に $this->tree_arr[] = array($no,"$pre$img"); //再帰 tree::make_tree($id,$no,$pre,$img); } return true; }else{ return false; } } /*** * HTML生成・テンプレ置換 * */ function show_msg($no, $act="", $mark=""){ global $PHP_SELF,$all,$n,$log; if($log) $kako = "&log=$log"; if(!$this->id[$no]) return false; list($id,$oya,$root,$subj,$name,$email,$date,$com,$url,$tim,$host,) = explode("\t", $this->id[$no]); switch($act){ case 'root': //if($oya == 0) $html = $this->root_msg; foreach($this->thread[$no] as $kidno){ rsort($kidno); $tmp = $kidno[0]; $max = ($tmp > $max) ? $tmp : $max; $resc += count($kidno); } $resc = $resc - 1; list($lno,$oya,$root,,$lname,$lemail,$ldate,$lcom,$lurl,$ltim,$lhost,) = explode("\t", $this->id[$max]); $html = $this->root_msg; $lcom = substr($lcom, 0, 30).".."; $html = str_replace('{res}', "$resc", $html); $html = str_replace('{lname}', "$lname", $html); $html = str_replace('{ldate}', "$ldate", $html); $html = str_replace('{lcom}', "$lcom", $html); break; case 'expn': $html = ($oya == 0) ? $this->expn_oya : $this->expn_res; if($email && $this->if_email=="") $html = str_replace('{name}', "{name}", $html); elseif($email) $html = str_replace('{email}', "$this->if_email", $html); else $html = str_replace('{email}', "", $html); break; case 'tree': if($oya == 0){ $html = $this->tree_oya; }else{ $html = $mark . $this->tree_res; } if($id == $n) $html = ereg_replace("(]+)>)([^<]+)()","\\3", $html); if($all) $html = str_replace('{n}', "#$id", $html); if(!$all && !$n && (strlen($name)+strlen($subj)+strlen($mark) > $this->cut_size)){ if(strlen($name) > strlen($subj)){ $name = substr($name, 0, $this->cut_size-strlen($mark)+strlen($subj)) . ".."; }else{ $subj = substr($subj, 0, $this->cut_size-strlen($mark)+strlen($name)) . ".."; } } break; case 'dump': $html = $this->dump_msg; if($email && $this->if_email=="") $html = str_replace('{name}', "{name}", $html); elseif($email) $html = str_replace('{email}', "$this->if_email", $html); else $html = str_replace('{email}', "", $html); if($id == $n) $html = str_replace(''."([^<]+)", "", $html); } if($url) $html = str_replace('{url}',"$this->if_url", $html); else $html = str_replace('{url}', "", $html); // 自動リンク $com = tree::auto_link($com); // >がある時は色変更 $com = ereg_replace("(^|>)(>[^<]*)", "\\1quote>\\2", $com); $html = str_replace('{id}', $id, $html); $html = str_replace('{subj}', $subj, $html); $html = str_replace('{name}', $name, $html); $html = str_replace('{date}', $date, $html); $html = str_replace('{com}', $com, $html); $html = str_replace('{host}', $host, $html); $html = str_replace('{time}', $tim, $html); $html = str_replace('{n}', $PHP_SELF."?n=".$id.$kako, $html); $html = str_replace('{all}', $PHP_SELF."?all=".$root.$kako, $html); $html = str_replace('{reply}', $PHP_SELF."?n=".$id.$kako."#frm", $html); $html = str_replace('{nl}', "\n", $html); if(time() - $tim < $this->expire*3600) $html = str_replace('{new}', $this->new_msg, $html); else $html = str_replace('{new}', "", $html); return $html; } /*** * 書き込み前チェック * */ function add_chk($root=0, $oya=0, $name, $email, $subj, $com, $url, $pass, $email_reply, $no=''){ global $REQUEST_METHOD,$PHP_SELF; if ($_SESSION['time'] == "") { $this->error = "不正な投稿をしないで下さい"; return false; } if (time() - $_SESSION['time'] < $this->posttime) { $this->error = "投稿はもうしばらく時間を置いてからお願い致します"; return false; } // 数字化 if (!is_numeric($root) || !is_numeric($oya)) { $this->error = "不正な投稿をしないで下さい"; return false; } if ($no != "") $no = intval($no); if (preg_match("/(]*?>|\[url(?:\s?=|\]))|href=/i", $com)) { $this->error = "禁止ワードエラー!!"; return false; } if($REQUEST_METHOD != "POST") { $this->error = "不正な投稿をしないで下さい"; return false; } if($url && !ereg("^http://", $url)){ $this->error = "URLはhttp://から記入してください"; return false; } if($email && !ereg(".+@.+\\..+", $email)){ $this->error = "メールアドレスは正しく記入してください"; return false; } if($name=="" || ereg("^( | )*$",$name)){ $this->error = "名前が書き込まれていません"; return false; } if($subj=="" || ereg("^( | )*$",$subj)){ $this->error = "題名が書き込まれていません"; return false; } if($com=="" || ereg("^( | |[\r\n])*$",$com)){ $this->error = "本文が書き込まれていません"; return false; } // 親チェック、深さチェック、レス数チェック if($root != 0 && $no == ''){ if(!is_array($this->thread[$root])){ $this->error = "返信先の記事が見つかりませんでした"; return false; } if(count($this->thread[$root]) > $this->max_depth){ $this->error = "ツリー階層が深くなりすぎたので
これ以上返信できません"; return false; } foreach($this->thread[$root] as $kidno){ $resc += count($kidno); } if($resc > $this->max_res){ $this->error = "レス数制限に達したので
これ以上返信できません"; return false; } } if($no == "kako"){ $this->error = "過去ログには投稿できません"; return false; } //現在時刻 $tim = time(); // ホスト名を取得 $ip = (getenv("HTTP_X_FORWARDED_FOR") != "") ? getenv("HTTP_X_FORWARDED_FOR") : getenv("REMOTE_ADDR"); $host = gethostbyaddr($ip); //同一ホストからの連続投稿禁止 list($lno,,,,$lname,,,$lcom,,$ltime,$lhost,) = explode("\t", $this->logarr[0]); if($this->renzoku && $host==$lhost && $tim - $ltime < $this->renzoku){ $this->error = "連続投稿はもうしばらく時間を置いてからお願い致します"; return false; } //時間のフォーマット $date = gmdate("m/d H:i",$tim+9*60*60); //url整形 $url = trim($url); $url = ereg_replace("^http://", "", $url); $url = str_replace(" ", "%20", $url); //PW暗号化 if(trim($pass)!="") $pwd = substr(md5($pass),2,8); //テキスト整形 $subj = tree::CleanStr($subj); $name = tree::CleanStr($name); $email= tree::CleanStr($email); $url = tree::CleanStr($url); $com = tree::CleanStr($com,1); // 改行文字の統一。 $com = str_replace("\r\n", "\n", $com); $com = str_replace("\r", "\n", $com); if(substr_count($com, "\n") > $this->max_line){ $this->error = "コメント行数が長すぎます"; return false; } // 連続する空行を一行・\n→
\n→\n除去 $com = ereg_replace("\n(( | )*\n){3,}","\n",$com); $com = nl2br($com); $com = str_replace("\n", "", $com); // 二重投稿チェック if($no == '' && $name == $lname && $com == $lcom){ $this->error = "二重投稿は禁止です"; return false; } // 新NO、親は自分がroot if($no == ''){ $no = $lno + 1; if($root == 0) $root = $no; } elseif(!$this->id[$no]){ $this->error = "該当記事がみつかりません"; return false; } $no = intval($no); $oya = intval($oya); $root = intval($root); $dat = array($no,$oya,$root,$subj,$name,$email,$date,$com,$url,$tim,$host,$pwd,$email_reply); // クッキー保存 $cookvalue = implode(",", array($name,$email,$url,$pass)); setcookie ($this->cookie_n, $cookvalue,time()+14*24*3600); return $dat; } /*** * 新規書き込み * */ function add_post($dat){ list($no,$oya,$root,$subj,$name,$email,$date,$com,$url,$tim,$host,$pwd,$email_reply) = $dat; // データフォーマット $new_msg = implode("\t", $dat)."\n"; // $new_msg = "$no\t$oya\t$root\t$subj\t$name\t$email\t$date\t$com\t$url\t$tim\t$host\t$pwd\t$email_reply\n"; $mailbody = str_replace(">", ">", $com); $mailbody = str_replace("<", "<", $mailbody); $mailbody = str_replace(""e;", '"', $mailbody); $mailbody = str_replace("&", "&", $mailbody); $mailbody = eregi_replace("", "\n", $mailbody); // 管理人にメール通知 if($this->adm_mail && ereg(".+@.+\\..+", $this->adm_mail)){ $subject = str_replace("{id}", "$no", $this->adm_subj); $subject = str_replace("{subj}", $subj, $subject); $body = "Date: $date\nHost: $host\n"; $body.= "---------------------------------------------\n"; $body.= $mailbody; tree::send_mail($name, $email, $this->adm_mail, $subject, $body); } // 新ログ配列生成 if($oya != 0){ // レスがあったらメール list(,,,,,$o_email,,,,,,,$reply) = explode("\t", $this->id[$oya]); if(rtrim($reply) == "y" && ereg(".+@.+\\..+", $o_email)){ $subject = $no."-".$subj; $body = "$name さんからのレス\n"; $body.= "Date: $date\nHost: $host\n"; $body.= "---------------------------------------------\n"; $body.= $mailbody; tree::send_mail($name, $email, $o_email, $subject, $body); } for ($i = 0; $i < count($this->logarr); $i++) { list($id, $oy, $rt) = explode("\t", $this->logarr[$i]); if($root == $rt) $age[] = $this->logarr[$i]; else $buf[] = $this->logarr[$i]; } $this->logarr = array_merge($age, $buf); } array_unshift($this->logarr, $new_msg); // 過去ログ if($this->past){ // ログ数オーバーは過去ログに書き出す if(count($this->logarr) > $this->max_log){ $back = array_pop($this->logarr); $backline[] = $back; list($bid, $boya, $broot) = explode("\t", $back); for ($j = count($this->logarr); $j >= 0; $j--){ list($id, $oy, $root) = explode("\t", $this->logarr[$j]); if($bid == $root){ array_unshift($backline, $this->logarr[$j]); array_pop($this->logarr); } } // 現No取得 $no = file($this->pastno); // 現過去ログ読み込み if(file_exists($this->pastdir.$no[0].".log")) { $pastline = file($this->pastdir.$no[0].".log"); } // 現過去ログ行数オーバーならNOアップ if(count($pastline) > $this->max_log){ $no[0]++; $fp = fopen($this->pastno, "w"); fputs($fp, $no[0]); fclose($fp); }else{ $backline = array_merge($backline, $pastline); } // 過去ログ更新 $fp = fopen($this->pastdir.$no[0].".log", "w"); flock($fp, LOCK_EX); fputs($fp, implode('', $backline)); fclose($fp); } } // ログ書き込み $fp = fopen($this->logfile, "w"); flock($fp, LOCK_EX); fputs($fp, implode('', $this->logarr)); fclose($fp); return true; } /*** * 編集書き込み * */ function edit_post($dat){ list($no,$oya,$root,$subj,$name,$email,$date,$com,$url,$tim,$host,$pwd,$email_reply) = $dat; // データフォーマット $new_msg = implode("\t", $dat)."\n"; for($i = 0; $i < count($this->logarr); $i++){ list($id,) = explode("\t", $this->logarr[$i]); if($id == $no){ $this->logarr[$i] = $new_msg; break; } } // ログ書き込み $fp = fopen($this->logfile, "w"); flock($fp, LOCK_EX); fputs($fp, implode('', $this->logarr)); fclose($fp); return true; } /*** * タグ・¥除去 * */ function CleanStr($str,$com=0){ //先頭と末尾の空白除去 $str = trim($str); //区切り文字\t変換 $str = str_replace("\t", " ", $str); //¥を削除 if(get_magic_quotes_gpc()){ $str = stripslashes($str); } if(isset($this->bad_word)) $str = ereg_replace($this->bad_word, "**", $str); if($this->allow_tag && $com == 1){ //許可タグ以外除去 // $str = strip_tags($str, $this->allow_tag); $str = htmlspecialchars($str); $tags = str_replace("><", "|", $this->allow_tag); $tags = ereg_replace("[<>]", "", $tags); $str = preg_replace("/<($tags)\b(.*?)>/si", "<\\1\\2>", $str); $str = eregi_replace("<\/($tags)>", "", $str); $str = eregi_replace("<(.*)(style|onmouse|onclick|script)[^=]*=", "<\\1", $str); }else{ //タグ変換 $str = htmlspecialchars($str); } if ($com == 0) { $str = eregi_replace("[\r\n]", "", $str); } //特殊文字 $str = str_replace("&#", "&#", $str); $str = str_replace(""", '"', $str); return $str; } /*** * 削除 * */ function del_msg($id, $pwd){ if(!isset($this->id[$id])){ $this->error = "該当記事が見つかりません"; return false; } if(trim($pwd)==""){ $this->error = "パスワードを入力してください"; return false; } list($no,$oya,$root,,,,,,,,,$pass,) = explode("\t", $this->id[$id]); if($this->adm_pass == $pwd || substr(md5($pwd),2,8) == $pass){ $this->id[$id] = "$no\t$oya\t$root\t削除\t\t\t\tこの記事は削除されました\t\t\t\t\t\n"; $fp = fopen($this->logfile, "w"); flock($fp, LOCK_EX); fputs($fp, implode('', $this->id)); fclose($fp); } else { $this->error = "パスワードが違います"; return false; } return true; } /*** * 編集 * */ function edit_msg($id, $pwd){ if(!isset($this->id[$id])){ $this->error = "該当記事が見つかりません"; return false; } if(trim($pwd)==""){ $this->error = "パスワードを入力してください"; return false; } list($no,$oya,$root,$subj,$name,$email,,$com,$url,,,$pass,) = explode("\t", $this->id[$id]); if($this->adm_pass == $pwd || substr(md5($pwd),2,8) == $pass){ $com = eregi_replace("", "\r", $com); $html = tree::show_form($root, $oya, $subj, $com, "edit", $name, $email, $url, $no, $pwd); } else { $this->error = "パスワードが違います"; return false; } return $html; } /*** * 検索 * */ function search($word, $andor){ global $log; if(!isset($word)) return false; if(get_magic_quotes_gpc()) $word = stripslashes($word); $keys = preg_split("/( | )+/", trim($word)); foreach($this->logarr as $line){ $find = FALSE; for($i = 0; $i < count($keys); $i++){ if($keys[$i]=="") continue; if(stristr($line,$keys[$i])){ $find = TRUE; list($id,) = explode("\t", $line); }elseif($andor == "and"){ $find = FALSE; break; } } if($find) $result[] = $id; } if(is_array($result)){ $word = htmlspecialchars($word); $html.= "
$word の検索結果 ".count($result)."件
\n"; foreach($result as $no){ $html.= tree::show_msg($no, 'dump'); } } return $html; } /*** * メール送信 * */ function send_mail($name, $from, $to, $subject, $body){ if($subject!="") $subject = tree::mime_enc($subject); $body = str_replace("\r\n", "\n", $body); $body = str_replace("\r", "\n", $body); //$str = mb_convert_encoding($str, "JIS", "SJIS"); $name = tree::mime_enc($name); $froma = ($from) ? "$name<$from>" : "$name"; $head = "From: $froma\n"; $head.= "X-REF: ".getenv("HTTP_REFERER"); @mail($to, $subject, $body, $head); } /*** * MIMEエンコ * */ function mime_enc($str){ //$str = mb_convert_encoding($str, "JIS", "SJIS"); $encode = "=?iso-2022-jp?B?" . base64_encode($str) . "?="; //Bヘッダ+エンコード return $encode; } /*** * ヘッダ取得 * */ function show_head(){ if(file_exists("dcount.php")) include("dcount.php"); $this->head = str_replace("{today}", $today, $this->head); $this->head = str_replace("{yesterday}", $yesterday, $this->head); $this->head = str_replace("{total}", $total, $this->head); return $this->head; } /*** * フッタ取得 * */ function show_foot(){ $foot = str_replace("","

レッツPHP!",$this->foot);//消すな return $foot; } /*** * ツリー構造配列取得 * */ function get_tree(){ return $this->tree_arr; } /*** * 指定Noのデータ取得 * */ function get_line($no){ return explode("\t", $this->id[$no]); } /*** * クッキー名取得 * */ function cookie_s(){ return $this->cookie_s; } /*** * オートリンク * */ function auto_link($uri){ $uri = ereg_replace("(^|[^=\"'])(https?|ftp|news)(://[[:alnum:]\+\$\;\?\.%,!#~*/:@&=_-]+)","\\1\\2\\3",$uri); return $uri; } /*** * エラーメッセージ * */ function error_msg($msg='') { if(!$msg) $msg = $this->error; $html = "
[ エラー!! ]

$msg

戻る
\n"; return $html; } /*** * コマンド実行後のジャンプ * */ function jump_url($url) { echo ''; } } /*-------------メイン------------*/ $d = new tree($mode, $log); $d->get_arr(); switch($act) { case 'post': if($dat = $d->add_chk($st, $re, $name, $email, $sub, $com, $url, $pass, $email_reply)){ $d->add_post($dat); $d->jump_url($PHP_SELF); } else { echo $d->show_head(); echo $d->error_msg(); echo $d->show_foot(); } exit; case 'editpost': if($dat = $d->add_chk($st, $re, $name, $email, $sub, $com, $url, $pass, $email_reply, $editno)){ $d->edit_post($dat); $d->jump_url($PHP_SELF); } else { echo $d->show_head(); echo $d->error_msg(); echo $d->show_foot(); } exit; case 'del': if(!$d->del_msg($id, $pwd)){ echo $d->show_head(); echo $d->error_msg(); echo $d->show_foot(); } else { $d->jump_url($PHP_SELF); } exit; case 'edit': if($html = $d->edit_msg($id, $pwd)){ echo $d->show_head(); echo $html; echo $d->show_foot(); } else { echo $d->show_head(); echo $d->error_msg(); echo $d->show_foot(); } exit; case 'setup_cookie': $cookvalue = implode(",", array($view,$pagetree,$pageexpn,$pageroot,$pagedump)); setcookie($d->cookie_s(), $cookvalue,time()+365*24*3600); $d->jump_url($PHP_SELF); exit; } echo $d->show_head(); echo $d->show_navi(); /* 記事単独表示 */ if(is_numeric($n)){ echo $d->show_msg($n,'dump'); list($id, $oya, $root,$subj,,,,$com) = $d->get_line($n); echo $d->n_tree_st; $d->make_tree($root); $trees = $d->get_tree(); foreach($trees as $val){ echo $d->show_msg($val[0],'tree',$val[1]); } echo $d->n_tree_to; echo $d->show_form($root, $id, $subj, $com, "quote"); echo $d->show_del($n); echo $d->show_foot(); exit; } /* ツリー単独表示 */ if(is_numeric($all)){ $d->make_tree($all); $trees = $d->get_tree(); $d->all_tree_st = str_replace("{id}", "$all", $d->all_tree_st); echo $d->all_tree_st; foreach($trees as $val){ echo $d->show_msg($val[0],'tree',$val[1]); } echo $d->all_tree_to; $trees = $d->get_tree(); foreach($trees as $val){ echo $d->show_msg($val[0],'dump'); } echo $d->show_foot(); exit; } switch($mode){ case 'form': echo $d->show_form(); break; case 'search': echo $d->show_search($word); echo $d->search($word, $andor); break; case 'setup': echo $d->show_setup(); break; case 'past': echo $d->show_past(); break; default: echo $d->show_main($page); } echo $d->show_navi(); echo $d->show_foot(); ?>