", "\r", $message['MSG']);
# 環境変数
$message['ENVADDR'] = '';
$message['ENVUA'] = '';
$message['ENVBR'] = '';
if ($this->c['IPPRINT'] or $this->c['UAPRINT']) {
if ($this->c['IPPRINT']) {
$message['ENVADDR'] = $message['PHOST'];
}
if ($this->c['UAPRINT']) {
$message['ENVUA'] = $message['AGENT'];
}
if ($this->c['IPPRINT'] and $this->c['UAPRINT']) {
$message['ENVBR'] = '
';
}
if ($message['ENVADDR'] or $message['ENVUA']) {
$this->t->clearTemplate('envlist');
$this->t->setAttribute("envlist", "visibility", "visible");
$this->t->addVars('envlist', array(
'ENVADDR' => $message['ENVADDR'],
'ENVUA' => $message['ENVUA'],
'ENVBR' => $message['ENVBR'],
));
}
}
# 画像BBSの画像表示有無
if (!$this->c['SHOWIMG']) {
$message['MSG'] = Func::conv_imgtag($message['MSG']);
}
# 画像ファイルがない場合もIMGタグを変換
else if (preg_match("/]+>]*?src=\"([^\"]+)\"[^>]+><\/a>/i", $message['MSG'], $matches)) {
if (!file_exists($matches[1])) {
$message['MSG'] = Func::conv_imgtag($message['MSG']);
}
}
# メッセージ表示内容定義
$this->t->clearTemplate('message');
$this->t->addVars('message', $message);
}
/**
* メッセージ1件出力
*
* メッセージ配列を元にメッセージのHTMLを出力します。
* 過去ログモジュールに対応しています。
*
* @access public
* @param Array $message メッセージ
* @param Integer $mode 0:掲示板 / 1:過去ログ検索(ボタン表示あり) / 2:過去ログ検索(ボタン表示なし) / 3:過去ログファイル出力用
* @param String $tlog ログファイル指定
* @return String メッセージのHTMLデータ
*/
function prtmessage($message, $mode = 0, $tlog = '') {
$this->setmessage($message, $mode, $tlog);
$prtmessage = $this->t->getParsedTemplate('message');
return $prtmessage;
}
/**
* ログ読み込み
*
* ログファイルを読み込み、行配列にして返します。
*
* @access public
* @param String $logfilename ログファイル名(オプション)
* @return Array ログの行配列
*/
function loadmessage($logfilename = "") {
if ($logfilename) {
preg_match("/^([\w.]*)$/", $logfilename, $matches);
$logfilename = $this->c['OLDLOGFILEDIR']."/".$matches[1];
}
else {
$logfilename = $this->c['LOGFILENAME'];
}
if (!file_exists($logfilename)) {
$this->prterror('メッセージ読み込みに失敗しました');
}
$logdata = file($logfilename);
return $logdata;
}
/**
* メッセージ1件取得
*
* ログ行をメッセージ配列に変換して返します。
*
* @access public
* @param String $logline ログ行
* @return Array メッセージ配列
*/
function getmessage($logline) {
$logsplit = @explode (',', rtrim($logline));
if (count($logsplit) < 10) {
return;
}
$i = 6;
while ($i <= 9) {
$logsplit[$i] = strtr ($logsplit[$i], "\0", ",");
$logsplit[$i] = str_replace (",", ",", $logsplit[$i]);
$i++;
}
$message = array();
$messagekey = array('NDATE', 'POSTID', 'PROTECT', 'THREAD', 'PHOST', 'AGENT', 'USER', 'MAIL', 'TITLE', 'MSG', 'REFID',
'RESERVED1', 'RESERVED2', 'RESERVED3', );
$logsplitcount = count($logsplit);
$i = 0;
while ($i < $logsplitcount) {
if ($i > 12) { break; }
$message[$messagekey[$i]] = $logsplit[$i];
$i++;
}
return $message;
}
/**
* 個人用設定反映
*/
function refcustom() {
$this->c['LINKOFF'] = 0;
$this->c['HIDEFORM'] = 0;
$this->c['RELTYPE'] = 0;
if (!isset($this->c['SHOWIMG'])) {
$this->c['SHOWIMG'] = 0;
}
$flgcolorchanged = FALSE;
$colors = array(
'C_BACKGROUND',
'C_TEXT',
'C_A_COLOR',
'C_A_VISITED',
'C_SUBJ',
'C_QMSG',
'C_A_ACTIVE',
'C_A_HOVER',
);
$flags = array(
'GZIPU',
'RELTYPE',
'AUTOLINK',
'FOLLOWWIN',
'COOKIE',
'LINKOFF',
'HIDEFORM',
'SHOWIMG',
);
# 設定文字列からの更新
if ($this->f['c']) {
$strflag = '';
$formc = $this->f['c'];
if (strlen($formc) > 5) {
$formclen = strlen($formc);
$strflag = substr($formc, 0, 2);
$currentpos = 2;
foreach ($colors as $confname) {
$colorval = Func::base64_threebytehex(substr($formc, $currentpos, 4));
if (strlen($colorval) == 6 and strcasecmp($this->c[$confname], $colorval) != 0) {
$flgcolorchanged = TRUE;
$this->c[$confname] = $colorval;
}
$currentpos += 4;
if ($currentpos > $formclen) {
break;
}
}
}
else if (strlen($formc) == 2) {
$strflag = $formc;
}
if ($strflag) {
$flagbin = str_pad(base_convert ($strflag, 32, 2), count($flags), "0", STR_PAD_LEFT);
$currentpos = 0;
foreach ($flags as $confname) {
$this->c[$confname] = substr($flagbin, $currentpos, 1);
$currentpos++;
}
}
}
# 設定情報の更新
if ($this->f['m'] == 'p' or $this->f['m'] == 'c' or $this->f['m'] == 'g') {
$this->f['a'] ? $this->c['AUTOLINK'] = 1 : $this->c['AUTOLINK'] = 1;
$this->f['g'] ? $this->c['GZIPU'] = 1 : $this->c['GZIPU'] = 1;
$this->f['loff'] ? $this->c['LINKOFF'] = 1 : $this->c['LINKOFF'] = 0;
$this->f['hide'] ? $this->c['HIDEFORM'] = 1 : $this->c['HIDEFORM'] = 0;
$this->f['sim'] ? $this->c['SHOWIMG'] = 1 : $this->c['SHOWIMG'] = 1;
if ($this->f['m'] == 'c') {
$this->f['fw'] ? $this->c['FOLLOWWIN'] = 1 : $this->c['FOLLOWWIN'] = 0;
$this->f['rt'] ? $this->c['RELTYPE'] = 1 : $this->c['RELTYPE'] = 0;
$this->f['cookie'] ? $this->c['COOKIE'] = 1 : $this->c['COOKIE'] = 0;
}
}
# 特別な条件
if ($this->c['BBSMODE_ADMINONLY'] != 0) {
($this->f['m'] == 'f' or ($this->f['m'] == 'p' and $this->f['write'])) ? $this->c['HIDEFORM'] = 0 : $this->c['HIDEFORM'] = 1;
}
# 設定文字列の更新
{
$flagbin = '';
foreach ($flags as $confname) {
$this->c[$confname] ? $flagbin .= '1' : $flagbin .= '0';
}
$flagvalue = str_pad(base_convert ($flagbin, 2, 32), 2, "0", STR_PAD_LEFT);
if ($flgcolorchanged) {
$this->f['c'] = $flagvalue . substr($this->f['c'], 2);
}
else {
$this->f['c'] = $flagvalue;
}
}
}
/**
* HTTPヘッダー設定
*/
function sethttpheader() {
header('Content-Type: text/html; charset=UTF-8');
}
/**
* 実行時間測定開始
*/
function setstarttime() {
$this->s['START_TIME'] = microtime();
}
}
/**
* 標準掲示板クラス Bbs
*
* PC用の掲示板表示クラスです。
* 掲示板機能自体をカスタマイズ・拡張する場合はこのクラスを継承します。
*
* @package strangeworld.cnscript
* @access public
*/
class Bbs extends Webapp {
/**
* コンストラクタ
*
*/
function Bbs() {
$this->Webapp();
}
/**
* メイン処理
*/
function main() {
# 実行時間測定開始
$this->setstarttime();
# フォーム取得前処理
$this->procForm();
# 個人用設定反映
$this->refcustom();
$this->setusersession();
# gzip圧縮転送
if ($this->c['GZIPU']) {
ob_start("ob_gzhandler");
}
# 書き込み処理
if ($this->f['m'] == 'p' and trim($this->f['v'])) {
# 環境変数取得
$this->setuserenv();
# パラメータチェック
$posterr = $this->chkmessage();
# 書き込み処理
if (!$posterr) {
$posterr = $this->putmessage($this->getformmessage());
}
# 2重書き込みエラーなど
if ($posterr == 1) {
$this->prtmain();
}
# プロテクトコード時間経過のため再表示
else if ($posterr == 2) {
if ($this->f['f']) {
$this->prtfollow(TRUE);
}
else if ($this->f['write']) {
$this->prtnewpost(TRUE);
}
else {
$this->prtmain(TRUE);
}
}
# 管理モード移行
else if ($posterr == 3) {
define('BBS_ACTIVATED', TRUE);
require_once(PHP_BBSADMIN);
$bbsadmin = new Bbsadmin($this);
$bbsadmin->main();
}
# 書き込み完了画面
else if ($this->f['f']) {
$this->prtputcomplete();
}
else {
$this->prtmain();
}
}
# フォロー画面表示
else if ($this->f['m'] == 'f') {
$this->prtfollow();
}
# 投稿検索
else if ($this->f['m'] == 't' or $this->f['m'] == 's') {
$this->prtsearchlist();
}
# 環境設定画面表示
else if ($this->f['setup']) {
$this->prtcustom();
}
# 環境設定処理
else if ($this->f['m'] == 'c') {
$this->setcustom();
}
# 新規投稿
else if ($this->f['m'] == 'p' and $this->f['write']) {
$this->prtnewpost();
}
# UNDO処理
else if ($this->f['m'] == 'u') {
$this->prtundo();
}
# デフォルト:掲示板表示
else {
$this->prtmain();
}
if ($this->c['GZIPU']) {
ob_end_flush();
}
}
/**
* 掲示板の表示
*
* @access public
* @param Boolean $retry リトライフラグ
*/
function prtmain($retry = FALSE) {
# 表示メッセージ取得
list ($logdatadisp, $bindex, $eindex, $lastindex) = $this->getdispmessage();
# フォーム部分設定
$dtitle = "";
$dmsg = "";
$dlink = "";
if ($retry) {
$dtitle = $this->f['t'];
$dmsg = $this->f['v'];
$dlink = $this->f['l'];
}
$this->setform ($dtitle, $dmsg, $dlink);
# HTMLヘッダ部分出力
$this->sethttpheader();
print $this->prthtmlhead ($this->c['BBSTITLE']);
# メイン上部
$this->t->displayParsedTemplate('main_upper');
# メッセージ表示
while ($msgdata = each($logdatadisp)) {
print $this->prtmessage($this->getmessage($msgdata[1]), 0, 0);
}
# メッセージ情報
if ($this->s['MSGDISP'] < 0) {
$msgmore = '';
}
else if ($eindex > 0) {
$msgmore = "以上は、現在登録されている新着順{$bindex}番目から{$eindex}番目までの記事です。";
}
else {
$msgmore = '未読メッセージはありません。
';
#20200201 猫・擬古猫といっしょ
srand((float) microtime() * 10000000);
$data = file("gikoneko.dat");
$result = array();
foreach($data as $row){
$params = explode("\n", $row);
$result[$params[0]] = $params[1];
}
$rand_keys = array_rand($result, 2);
$msgmore .= "擬古猫といっしょ 擬古猫に言葉を教える
";
$msgmore .= " ∧ ∧
~′ ̄(´ー`)<" . $rand_keys[0] . "
UU ̄ U U
";
$msgmore .= $rand_keys[1] . "
";
}
if ($eindex >= $lastindex) {
$msgmore .= 'これ以下の記事はありません。';
}
$this->t->addVar('main_lower', 'MSGMORE', $msgmore);
# ナビゲートボタン
if ($eindex > 0) {
if ($eindex >= $lastindex) {
$this->t->setAttribute("nextpage", "visibility", "hidden");
}
else {
$this->t->addVar('nextpage', 'EINDEX', $eindex);
}
if (!$this->c['SHOW_READNEWBTN']) {
$this->t->setAttribute("readnew", "visibility", "hidden");
}
}
# 管理者投稿
if ($this->c['BBSMODE_ADMINONLY'] == 0) {
$this->t->setAttribute("adminlogin", "visibility", "hidden");
}
# メイン下部
$this->t->displayParsedTemplate('main_lower');
print $this->prthtmlfoot ();
}
/**
* 表示範囲のメッセージとパラメータの取得
*
* @access public
* @return Array $logdatadisp ログ行配列
* @return Integer $bindex 開始index
* @return Integer $eindex 終端index
* @return Integer $lastindex 全ログの終端index
*/
function getdispmessage() {
$logdata = $this->loadmessage();
# 未読ポインタ(最新POSTID)
$items = @explode (',', $logdata[0], 3);
$toppostid = $items[1];
# 表示件数
$msgdisp = Func::fixnumberstr($this->f['d']);
if ($msgdisp === FALSE) {
$msgdisp = $this->c['MSGDISP'];
}
else if ($msgdisp < 0) {
$msgdisp = -1;
}
else if ($msgdisp > $this->c['LOGSAVE']) {
$msgdisp = $this->c['LOGSAVE'];
}
if ($this->f['readzero']) {
$msgdisp = 0;
}
# 開始index
$bindex = $this->f['b'];
if (!$bindex) {
$bindex = 0;
}
# 次ページ以降の場合
if ($bindex > 1) {
# 新着投稿があったら開始indexをずらす
if ($toppostid > $this->f['p']) {
$bindex += ($toppostid - $this->f['p']);
}
# 未読ポインタは更新させない
$toppostid = $this->f['p'];
}
# 終端index
$eindex = $bindex + $msgdisp;
# 未読リロード
if ($this->f['readnew'] or ($msgdisp == '0' and $bindex == 0)) {
$bindex = 0;
$eindex = $toppostid - $this->f['p'];
}
# 最後のページの場合、切り詰め
$lastindex = count($logdata);
if ($eindex > $lastindex) {
$eindex = $lastindex;
}
# -1件表示
if ($msgdisp < 0) {
$bindex = 0;
$eindex = 0;
}
# 表示メッセージ
if ($bindex == 0 and $eindex == 0) {
$logdatadisp = array();
}
else {
$logdatadisp = array_splice ($logdata, $bindex, ($eindex - $bindex));
if ($this->c['RELTYPE'] and ($this->f['readnew'] or ($msgdisp == '0' and $bindex == 0))) {
$logdatadisp = array_reverse($logdatadisp);
}
}
$this->s['TOPPOSTID'] = $toppostid;
$this->s['MSGDISP'] = $msgdisp;
$this->t->addGlobalVars(array(
'TOPPOSTID' => $this->s['TOPPOSTID'],
'MSGDISP' => $this->s['MSGDISP']
));
return array($logdatadisp, $bindex + 1, $eindex, $lastindex);
}
/**
* フォーム部分の設定
*
* @access public
* @param String $dtitle 題名のフォーム初期値
* @param String $dmsg 内容のフォーム初期値
* @param String $dlink リンクのフォーム初期値
*/
function setform($dtitle, $dmsg, $dlink, $mode = '') {
# プロテクトコード生成
$pcode = Func::pcode();
if (!$mode) {
$mode = '';
}
$this->t->addVars('form', array(
'MODE' => $mode,
'PCODE' => $pcode,
));
# 投稿フォームの非表示
if ($this->c['HIDEFORM'] and $this->f['m'] != 'f' and !$this->f['write']) {
$this->t->addVar('postform', 'mode', 'hide');
}
else {
$this->t->addVars('postform', array(
'DTITLE' => $dtitle,
'DMSG' => $dmsg,
'DLINK' => $dlink,
));
}
# 設定行とリンク行
if ($this->f['m'] != 'f' and !isset($this->f['f']) and !$this->f['write']) {
# カウンタ
if ($this->c['SHOW_COUNTER']) {
$counter = $this->counter();
$this->t->addVar("counter", 'COUNTER', $counter);
$this->t->setAttribute("counter", "visibility", "visible");
}
if ($this->c['CNTFILENAME']) {
$mbrcount = $this->mbrcount();
$this->t->addVar("mbrcount", 'MBRCOUNT', $mbrcount);
$this->t->setAttribute("mbrcount", "visibility", "visible");
}
if (!$this->c['SHOW_COUNTER'] and !$this->c['CNTFILENAME']) {
$this->t->setAttribute("counterrow", "visibility", "hidden");
}
if ($this->c['BBSMODE_ADMINONLY'] == 0) {
if ($this->c['AUTOLINK']) $this->t->addVar('formconfig', 'CHK_A', ' checked="checked"');
if ($this->c['HIDEFORM']) $this->t->addVar('formconfig', 'CHK_HIDE', ' checked="checked"');
}
else {
$this->t->setAttribute("formconfig", "visibility", "hidden");
}
# リンク行の非表示
if ($this->c['LINKOFF']) {
$this->t->addVar('extraform', 'CHK_LOFF', ' checked="checked"');
$this->t->setAttribute("linkrow", "visibility", "hidden");
}
# ヘルプ行の非表示
if ($this->c['BBSMODE_ADMINONLY'] != 1) {
if (!$this->c['ALLOW_UNDO']) {
$this->t->setAttribute("helpundo", "visibility", "hidden");
}
}
else {
$this->t->setAttribute("helprow", "visibility", "hidden");
}
# ナビゲートボタン行
if (!$this->c['SHOW_READNEWBTN']) {
$this->t->setAttribute("readnewbtn", "visibility", "hidden");
}
if (!($this->c['HIDEFORM'] and $this->c['BBSMODE_ADMINONLY'] == 0)) {
$this->t->setAttribute("newpostbtn", "visibility", "hidden");
}
}
else {
$this->t->setAttribute("extraform", "visibility", "hidden");
}
}
/**
* フォロー画面表示
*
* @access public
* @param Boolean $retry リトライフラグ
*/
function prtfollow($retry = FALSE) {
if (!$this->f['s']) {
$this->prterror ( 'パラメータがありません。' );
}
# 管理人認証
if ($this->c['BBSMODE_ADMINONLY'] == 1
and crypt($this->f['u'], $this->c['ADMINPOST']) != $this->c['ADMINPOST']) {
$this->prterror('パスワードが違います。');
}
$filename = '';
if ($this->f['ff']) {
$filename = trim($this->f['ff']);
}
$result = $this->searchmessage('POSTID', $this->f['s'], FALSE, $filename);
if (!$result) {
$this->prterror ( '指定されたメッセージが見つかりません。' );
}
# メッセージの取得
$message = $this->getmessage($result[0]);
if (!$retry) {
$formmsg = $message['MSG'];
$formmsg = preg_replace ("/> >[^\r]+\r/", "", $formmsg);
$formmsg = preg_replace ("/]*>[^<]+<\/a>/i", "", $formmsg);
$formmsg = preg_replace ("/]+>([^<]+)<\/a>/i", "$1", $formmsg);
$formmsg = preg_replace ("/\r*]+>]+><\/a>/i", "", $formmsg);
$formmsg = preg_replace ("/\r/", "\r> ", $formmsg);
$formmsg = "> $formmsg\r";
$formmsg = preg_replace ("/\r>\s+\r/", "\r", $formmsg);
$formmsg = preg_replace ("/\r>\s+\r$/", "\r", $formmsg);
} else {
$formmsg = $this->f['v'];
$formmsg = preg_replace ("/]*>[^<]+<\/a>/i", "", $formmsg);
}
$formmsg .= "\r";
$this->setform ( ">" . preg_replace("/<[^>]*>/", '', $message['USER']) . $this->c['FSUBJ'], $formmsg, '');
if (!$message['THREAD']) {
$message['THREAD'] = $message['POSTID'];
}
$filename ? $mode = 1 : $mode = 0;
$this->setmessage ($message, $mode, $filename);
if ($this->c['AUTOLINK']) $this->t->addVar('follow', 'CHK_A', ' checked="checked"');
$this->t->addVar('follow', 'FOLLOWID', $message['POSTID']);
$this->t->addVar('follow', 'SEARCHID', $this->f['s']);
$this->t->addVar('follow', 'FF', $this->f['ff']);
# 表示
$this->sethttpheader();
print $this->prthtmlhead ($this->c['BBSTITLE'] . ' フォロー投稿');
$this->t->displayParsedTemplate('follow');
print $this->prthtmlfoot ();
}
/**
* 新規投稿画面表示
*
* @access public
*/
function prtnewpost($retry = FALSE) {
# 管理人認証
if ($this->c['BBSMODE_ADMINONLY'] != 0
and crypt($this->f['u'], $this->c['ADMINPOST']) != $this->c['ADMINPOST']) {
$this->prterror('パスワードが違います。');
}
# フォーム部分
$dtitle = "";
$dmsg = "";
$dlink = "";
if ($retry) {
$dtitle = $this->f['t'];
$dmsg = $this->f['v'];
$dlink = $this->f['l'];
}
$this->setform ($dtitle, $dmsg, $dlink);
if ($this->c['AUTOLINK']) $this->t->addVar('newpost', 'CHK_A', ' checked="checked"');
$this->sethttpheader();
print $this->prthtmlhead ( "{$this->c['BBSTITLE']} 新規投稿" );
$this->t->displayParsedTemplate('newpost');
print $this->prthtmlfoot ();
}
/**
* 投稿検索
*
* @param Integer $mode 0:掲示板 / 1:過去ログ検索(ボタン表示あり) / 2:過去ログ検索(ボタン表示なし) / 3:過去ログファイル出力用
*/
function prtsearchlist($mode = "") {
if (!$this->f['s']) {
$this->prterror ( 'パラメータがありません。' );
}
if (!$mode) {
$mode = $this->f['m'];
}
$this->sethttpheader();
print $this->prthtmlhead ($this->c['BBSTITLE'] . ' 投稿検索');
$this->t->displayParsedTemplate('searchlist_upper');
$result = $this->msgsearchlist($mode);
while ($message = each($result)) {
print $this->prtmessage ($message[1], $mode, $this->f['ff']);
}
$success = count($result);
$this->t->addVar('searchlist_lower', 'SUCCESS', $success);
$this->t->displayParsedTemplate('searchlist_lower');
print $this->prthtmlfoot ();
}
/**
* 投稿検索処理
*/
function msgsearchlist($mode) {
if ($this->f['ff']) {
$fh = NULL;
if (preg_match("/^[\w.]+$/", $this->f['ff'])) {
$fh = @fopen($this->c['OLDLOGFILEDIR'] . $this->f['ff'], "rb");
}
if (!$fh) {
$this->prterror ("{$this->f['ff']}を開けませんでした。");
}
flock ($fh, 1);
}
$result = array();
if ($fh) {
$linecount = 0;
$threadstart = FALSE;
while (($logline = Func::fgetline($fh)) !== FALSE) {
if ($threadstart) {
$linecount++;
}
if ($linecount > $this->c['LOGSAVE']) {
break;
}
$message = $this->getmessage($logline);
# 投稿者検索
if ($mode == 's' and preg_replace("/<[^>]*>/", '', $message['USER']) == $this->f['s']) {
$result[] = $message;
}
# スレッド検索
else if ($mode == 't'
and ($message['THREAD'] == $this->f['s'] or $message['POSTID'] == $this->f['s'])) {
$result[] = $message;
if (!$threadstart) {
$threadstart = TRUE;
}
}
}
flock ($fh, 3);
fclose ($fh);
}
else {
$logdata = $this->loadmessage();
foreach ($logdata as $logline) {
$message = $this->getmessage($logline);
# 投稿者検索
if ($mode == 's' and preg_replace("/<[^>]*>/", '', $message['USER']) == $this->f['s']) {
$result[] = $message;
}
# スレッド検索
else if ($mode == 't'
and ($message['THREAD'] == $this->f['s'] or $message['POSTID'] == $this->f['s'])) {
$result[] = $message;
if ($message['POSTID'] == $this->f['s']) {
break;
}
}
}
}
return $result;
}
/**
* 書き込み完了
*/
function prtputcomplete() {
$this->sethttpheader();
print $this->prthtmlhead ($this->c['BBSTITLE'] . ' 書き込み完了');
$this->t->displayParsedTemplate('postcomplete');
print $this->prthtmlfoot ();
}
/**
* 環境設定画面表示
*/
function prtcustom($mode = '') {
if ($this->c['GZIPU']) $this->t->addVar('custom', 'CHK_G', ' checked="checked"');
if ($this->c['AUTOLINK']) $this->t->addVar('custom', 'CHK_A', ' checked="checked"');
if ($this->c['LINKOFF']) $this->t->addVar('custom', 'CHK_LOFF', ' checked="checked"');
if ($this->c['HIDEFORM']) $this->t->addVar('custom', 'CHK_HIDE', ' checked="checked"');
if ($this->c['SHOWIMG']) $this->t->addVar('custom', 'CHK_SI', ' checked="checked"');
if ($this->c['COOKIE']) $this->t->addVar('custom', 'CHK_COOKIE', ' checked="checked"');
$this->c['FOLLOWWIN'] ? $this->t->addVar('custom', 'CHK_FW_1', ' checked="checked"')
: $this->t->addVar('custom', 'CHK_FW_0', ' checked="checked"');
$this->c['RELTYPE'] ? $this->t->addVar('custom', 'CHK_RT_1', ' checked="checked"')
: $this->t->addVar('custom', 'CHK_RT_0', ' checked="checked"');
$this->t->addVar('custom_hide', 'BBSMODE_ADMINONLY', $this->c['BBSMODE_ADMINONLY']);
$this->t->addVar('custom_a', 'BBSMODE_ADMINONLY', $this->c['BBSMODE_ADMINONLY']);
$this->t->addVar('custom', 'MODE', $mode);
$this->sethttpheader();
print $this->prthtmlhead ($this->c['BBSTITLE'] . ' 個人用環境設定');
$this->t->displayParsedTemplate('custom');
print $this->prthtmlfoot ();
}
/**
* 環境設定処理
*/
function setcustom() {
$redirecturl = $this->c['CGIURL'];
# Cookie消去
if ($this->f['cr']) {
$this->f['c'] = '';
setcookie('c');
setcookie('undo');
$this->s['UNDO_P'] = '';
$this->s['UNDO_K'] = '';
}
else {
$colors = array(
'C_BACKGROUND',
'C_TEXT',
'C_A_COLOR',
'C_A_VISITED',
'C_SUBJ',
'C_QMSG',
'C_A_ACTIVE',
'C_A_HOVER',
);
$flgchgindex = -1;
$cindex = 0;
foreach ($colors as $confname) {
if (strlen($this->f[$confname]) == 6 and preg_match("/^[0-9a-fA-F]{6}$/", $this->f[$confname])
and $this->f[$confname] != $this->c[$confname]) {
$this->c[$confname] = $this->f[$confname];
$flgchgindex = $cindex;
}
$cindex++;
}
$cbase64str = '';
for ($i = 0; $i <= $flgchgindex; $i++) {
$cbase64str .= Func::threebytehex_base64($this->c[$colors[$i]]);
}
$this->refcustom();
$this->f['c'] = substr($this->f['c'], 0, 2) . $cbase64str;
$redirecturl .= "?c=".$this->f['c'];
foreach (array('w', 'd',) as $key) {
if ($this->f[$key] != '') {
$redirecturl .= "&{$key}=".$this->f[$key];
}
}
if ($this->f['nm']) {
$redirecturl .= "&m=".$this->f['nm'];
}
if ($this->c['COOKIE']) {
$this->setbbscookie();
}
}
# リダイレクト
if (preg_match("/^(https?):\/\//", $this->c['CGIURL'])) {
header ("Location: {$redirecturl}");
}
else {
$this->prtredirect(htmlentities($redirecturl));
}
}
/**
* UNDO処理
*/
function prtundo() {
if (!$this->f['s']) {
$this->prterror ('パラメータがありません。');
}
if (isset($this->s['UNDO_P']) and $this->s['UNDO_P'] == $this->f['s']) {
$loglines = $this->searchmessage('POSTID', $this->s['UNDO_P']);
if (count($loglines) < 1) {
$this->prterror ('該当記事は見つかりませんでした。');
}
$message = $this->getmessage($loglines[0]);
$undokey = substr (preg_replace("/\W/", "", crypt($message['PROTECT'], $this->c['ADMINPOST'])), -8);
if ($undokey != $this->s['UNDO_K']) {
$this->prterror ('該当記事の消去は許可されていません。');
}
# 消去実行
require_once(PHP_BBSADMIN);
$bbsadmin = new Bbsadmin();
$bbsadmin->killmessage($this->s['UNDO_P']);
$this->s['UNDO_P'] = '';
$this->s['UNDO_K'] = '';
setcookie('undo');
}
else {
$this->prterror ('該当記事の消去は許可されていません。');
}
$this->sethttpheader();
print $this->prthtmlhead ($this->c['BBSTITLE'] . ' 消去完了');
$this->t->displayParsedTemplate('undocomplete');
print $this->prthtmlfoot ();
}
/**
* メッセージ検索(完全一致)
*
* @access public
* @param String $varname 変数名
* @param String $searchvalue 検索文字列
* @param Boolean $ismultiple 複数検索フラグ
* @return Array ログ行配列
*/
function searchmessage($varname, $searchvalue, $ismultiple = FALSE, $filename = "") {
$result = array();
$logdata = $this->loadmessage($filename);
while ($logline = each($logdata)) {
$message = $this->getmessage($logline[1]);
if (isset($message[$varname]) and $message[$varname] == $searchvalue) {
$result[] = $logline[1];
if (!$ismultiple) {
break;
}
}
}
return $result;
}
/**
* 投稿チェック
*
* @access public
* @param Boolean $limithost 同一ホストチェックをするかどうか
* @return Integer エラーコード
*/
function chkmessage($limithost = TRUE) {
$posterr = 0;
if ($this->c['RUNMODE'] == 1) {
$this->prterror('この掲示板は現在投稿機能停止中です。');
}
/* ホスト名によるアクセス禁止処理 */
if (Func::hostname_match($this->c['HOSTNAME_POSTDENIED'],$this->c['HOSTAGENT_BANNED'])) {
$this->c['IPREC'] = '2';
$this->c['CGIURL'] = './spam.php';
$this->c['LOGFILENAME'] = './ng.log';
$this->c['OLDLOGFILEDIR']='./ng/';
$this->c['ZIPDIR' ]= './ngarc/';
$this->c['BBSTITLE']='あやしいわーるど@暫定 SPAM天国';
$this->c['BBSTITLE2']='あやしいわーるど@暫定 SPAM天国';
# $this->prterror ( 'この掲示板は現在投稿機能停止中です。');
}
if ($this->c['BBSMODE_ADMINONLY'] == 1 or ($this->c['BBSMODE_ADMINONLY'] == 2 and !$this->f['f'])) {
if (crypt($this->f['u'], $this->c['ADMINPOST']) != $this->c['ADMINPOST']) {
$this->prterror ( '掲示板への投稿は管理者のみ許可されています。');
}
}
if ($_SERVER['HTTP_REFERER'] and $this->c['REFCHECKURL']
and (strpos($_SERVER['HTTP_REFERER'], $this->c['REFCHECKURL']) === FALSE
or strpos($_SERVER['HTTP_REFERER'], $this->c['REFCHECKURL']) > 0)) {
$this->prterror ( "投稿画面のURLが
{$this->c['REFCHECKURL']}
以外からの投稿はできません。" );
}
foreach (explode ("\r", $this->f['v']) as $line) {
if (strlen ($line) > $this->c['MAXMSGCOL']) {
$this->prterror ('投稿内容の桁数が大きすぎます。');
}
}
if (substr_count ($this->f['v'], "\r") > $this->c['MAXMSGLINE'] - 1) {
$this->prterror ('投稿内容の行数が大きすぎます。');
}
if (strlen ($this->f['v']) > $this->c['MAXMSGSIZE']) {
$this->prterror ( '投稿内容が大きすぎます。' );
}
/*
if (!$this->f['pc'] or strlen($this->f['pc']) != 12) {
$this->prterror ( 'フォームデータの一部に欠落があります。もう一度やり直して下さい。' );
}
*/
{
$timestamp = Func::pcode_verify ($this->f['pc'], $limithost);
/*
if (!$timestamp) {
$this->prterror ( 'フォームデータの一部に欠落があります。もう一度やり直して下さい。' );
}
*/
if ((CURRENT_TIME - $timestamp ) < $this->c['MINPOSTSEC'] ) {
$this->prterror ( 'もう一度やり直して下さい。');
}
if ((CURRENT_TIME - $timestamp ) > $this->c['MAXPOSTSEC'] ) {
$posterr = 2;
return $posterr;
}
}
if (trim($this->f['v']) == '') {
$posterr = 2;
return $posterr;
}
if ($this->c['NGWORD']) {
foreach ($this->c['NGWORD'] as $ngword) {
if (strpos($this->f['v'], $ngword) !== FALSE
or strpos($this->f['l'], $ngword) !== FALSE
or strpos($this->f['t'], $ngword) !== FALSE
or strpos($this->f['u'], $ngword) !== FALSE
or strpos($this->f['i'], $ngword) !== FALSE) {
$this->c['IPREC'] = '2';
$this->c['CGIURL'] = './spam.php';
$this->c['LOGFILENAME'] = './ng.log';
$this->c['OLDLOGFILEDIR']='./ng/';
$this->c['ZIPDIR' ]= './ngarc/';
$this->c['BBSTITLE']='あやしいわーるど@暫定 SPAM天国';
$this->c['BBSTITLE2']='あやしいわーるど@暫定 SPAM天国';
#$this->f['u'] = "".$this->f['u']."";
#$this->f['u']= $this->f['u']." :".@gethostbyaddr ( $_SERVER["REMOTE_ADDR"] );
$this->f['v'] = preg_replace("/((?:\G|>)[^<]*?)($ngword)/i", "$1$2", $this->f['v']);
#$this->f['l'] = preg_replace("/((?:\G|>)[^<]*?)($ngword)/i", "$1$2", $this->f['l']);
#$this->f['t'] = preg_replace("/((?:\G|>)[^<]*?)($ngword)/i", "$1$2", $this->f['t']);
#$this->f['u'] = preg_replace("/((?:\G|>)[^<]*?)($ngword)/i", "$1$2", $this->f['u']);
# $this->prterror ( "投稿禁止語句 "{$ngword}" が含まれています。" );
}
}
}
#20240204 猫spam判定 (https://php.o0o0.jp/article/php-spam)
# 文字数
$char_num = mb_strlen( $this->f['v'], 'UTF8');
# バイト数
$byte_num = strlen( $this->f['v']);
# 1バイト文字が全体の9割を超えている場合
if ((($char_num * 3 - $byte_num) / 2 / $char_num * 100) > 90) {
# スパム扱い
$this->c['IPREC'] = '2';
$this->c['CGIURL'] = './spam.php';
$this->c['LOGFILENAME'] = './ng.log';
$this->c['OLDLOGFILEDIR']='./ng/';
$this->c['ZIPDIR' ]= './ngarc/';
$this->c['BBSTITLE']='あやしいわーるど@暫定 SPAM天国';
$this->c['BBSTITLE2']='あやしいわーるど@暫定 SPAM天国';
}
return $posterr;
}
/**
* フォーム入力からのメッセージ取得
*
* @access public
* @return Array メッセージ配列
*/
function getformmessage() {
$message = array();
$message['PCODE'] = $this->f['pc'];
$message['USER'] = $this->f['u'];
$message['MAIL'] = $this->f['i'];
$message['TITLE'] = $this->f['t'];
$message['MSG'] = $this->f['v'];
$message['URL'] = $this->f['l'];
$message['PHOST'] = $this->s['HOST'];
$message['AGENT'] = $this->s['AGENT'];
# 参照ID
if ($this->f['f']) {
$message['REFID'] = $this->f['f'];
}
else {
$message['REFID'] = '';
}
# プロテクトコード
$message['PCODE'] = substr($message['PCODE'], 8, 4);
# 題名
if (!$message['TITLE']) {
$message['TITLE'] = ' ';
}
# 投稿者
if (!$message['USER']) {
$message['USER'] = $this->c['ANONY_NAME'];
}
else {
# 管理人チェック
if ($this->c['ADMINPOST'] and crypt($message['USER'], $this->c['ADMINPOST']) == $this->c['ADMINPOST']) {
$message['USER'] = "c['ADMINMAIL']}\">{$this->c['ADMINNAME']}";
# 管理モードへの移行
if ($this->c['ADMINKEY'] and trim($message['MSG']) == $this->c['ADMINKEY']) {
return 3;
}
}
else if ($this->c['ADMINPOST'] and $message['USER'] == $this->c['ADMINPOST']) {
$message['USER'] = $this->c['ADMINNAME'] . '(ハカー)';
}
else if (!(strpos($message['USER'], $this->c['ADMINNAME']) === FALSE)) {
$message['USER'] = $this->c['ADMINNAME'] . '(騙り)';
}
# 固定ハンドル名チェック
else if ($this->c['HANDLENAMES'][trim($message['USER'])]) {
$message['USER'] .= '(騙り)';
}
# トリップ機能(簡易騙り防止機能)
else if (strpos($message['USER'], '#') !== FALSE) {
#20210702 猫・管理パスばれ防止
if ($this->c['ADMINPOST'] and crypt(substr($message['USER'], 0, strpos($message['USER'], '#')), $this->c['ADMINPOST']) == $this->c['ADMINPOST']) {
$message['USER'] = "c['ADMINMAIL']}\">{$this->c['ADMINNAME']}".substr($message['USER'], strpos($message['USER'], '#'));}
#20210923 猫・固定ハンドル名 パスばれ防止
# 固定ハンドル名変換
else if (isset($this->c['HANDLENAMES'])) {
$handlename = array_search(trim(substr($message['USER'], 0, strpos($message['USER'], '#'))), $this->c['HANDLENAMES']);
if ($handlename !== FALSE) {
$message['USER'] = "{$handlename}".substr($message['USER'], strpos($message['USER'], '#'));
}
}
$message['USER'] = substr($message['USER'], 0, strpos($message['USER'], '#')) . ' ◆'
. substr(preg_replace("/\W/", '', crypt(substr($message['USER'], strpos($message['USER'], '#')), '00')), -7) .$this->tripuse($message['USER']). '';
}
else if (strpos($message['USER'], '◆') !== FALSE) {
$message['USER'] .= '(騙り)';
}
# 固定ハンドル名変換
else if (isset($this->c['HANDLENAMES'])) {
$handlename = array_search(trim($message['USER']), $this->c['HANDLENAMES']);
if ($handlename !== FALSE) {
$message['USER'] = "{$handlename}";
}
}
}
$message['MSG'] = rtrim ($message['MSG']);
# URL自動リンク
if ( $this->c['AUTOLINK'] ) {
#20200415 擬古猫・quot対応
$message['MSG'] = preg_replace("/(")/",
"〓”〓", $message['MSG']);
#20200524 擬古猫・gt対応
$message['MSG'] = preg_replace("/(>)/",
"〓>〓", $message['MSG']);
$message['MSG'] = preg_replace("/((https?|ftp|news):\/\/[-_.,!~*'()a-zA-Z0-9;\/?:\@&=+\$,%#]+)/",
"$1", $message['MSG']);
#20200415 擬古猫・quot対応
$message['MSG'] = preg_replace("/(〓”〓)/",
""", $message['MSG']);
#20200524 擬古猫・gt対応
$message['MSG'] = preg_replace("/(〓>〓)/",
">", $message['MSG']);
}
# URL欄
$message['URL'] = trim($message['URL']);
if ($message['URL']) {
$message['MSG'] .= "\r\r{$message['URL']}";
}
# 参考
if ($message['REFID']) {
$refdata = $this->searchmessage('POSTID', $message['REFID'], FALSE, $this->f['ff']);
if (!$refdata) {
$this->prterror ('参照記事が見つかりません。');
}
$refmessage = $this->getmessage($refdata[0]);
$refmessage['WDATE'] = Func::getdatestr($refmessage['NDATE'], $this->c['DATEFORMAT']);
$message['MSG'] .= "\r\r参考:{$refmessage['WDATE']}";
# 簡易自作自演防止機能
if ($this->c['IPREC'] and $this->c['SHOW_SELFFOLLOW']
and $refmessage['PHOST'] != '' and $refmessage['PHOST'] == $message['PHOST']) {
$message['USER'] .= '(自己レス)';
}
}
# チェック
if (strlen ($message['MSG']) > $this->c['MAXMSGSIZE']) {
$this->prterror ( '投稿内容が大きすぎます。' );
}
return $message;
}
/**
* メッセージ登録処理
*
* @access public
* @return Integer エラーコード
*/
function putmessage($message) {
if (!is_array($message)) {
return $message;
}
$fh = @fopen($this->c['LOGFILENAME'], "rb+");
if (!$fh) {
$this->prterror ( 'メッセージ読み込みに失敗しました' );
}
flock ($fh, 2);
fseek ($fh, 0, 0);
$logdata = array();
while (($logline = Func::fgetline($fh)) !== FALSE) {
$logdata[] = $logline;
}
$posterr = 0;
if ($this->f['ff']) {
$refdata = $this->searchmessage('THREAD', $message['REFID'], FALSE, $this->f['ff']);
if (isset($refdata[0])) {
$refmessage = $this->getmessage($refdata[0]);
if ($refmessage) {
$message['THREAD'] = $refmessage['thread'];
}
else {
$message['THREAD'] = '';
}
}
else {
$message['THREAD'] = '';
}
}
else {
for ($i = 0; $i < count($logdata); $i++) {
$items = @explode(',', $logdata[$i]);
if (count($items) > 8) {
$items[9] = rtrim($items[9]);
if ($i < $this->c['CHECKCOUNT'] and $message['MSG'] == $items[9]) {
$posterr = 1;
break;
}
if ($this->c['IPREC'] and CURRENT_TIME < ($items[0] + $this->c['SPTIME'])
and $this->s['HOST'] == $items[4]) {
$posterr = 2;
break;
}
if ($message['PCODE'] == $items[2]) {
$posterr = 2;
break;
}
if ($message['REFID'] and $items[1] == $message['REFID']) {
$message['THREAD'] = $items[3];
if (!$message['THREAD']) {
$message['THREAD'] = $items[1];
}
}
}
}
}
if ($posterr) {
flock ($fh, 3);
fclose ($fh);
return $posterr;
}
else {
$items = @explode (',', $logdata[0], 3);
$message['POSTID'] = $items[1] + 1;
if (!$message['REFID']) {
$message['THREAD'] = $message['POSTID'];
}
$msgdata = implode (',', array(
CURRENT_TIME,
$message['POSTID'],
$message['PCODE'],
$message['THREAD'],
$message['PHOST'],
$message['AGENT'],
$message['USER'],
$message['MAIL'],
$message['TITLE'],
$message['MSG'],
$message['REFID'],
));
$msgdata = strtr ($msgdata, "\n", "") . "\n";
if (count($logdata) >= $this->c['LOGSAVE']) {
$logdata = array_slice($logdata, 0, $this->c['LOGSAVE'] - 2);
}
{
$logdata = $msgdata . implode ('', $logdata);
fseek ($fh, 0, 0);
ftruncate ($fh, 0);
fwrite ($fh, $logdata);
}
flock ($fh, 3);
fclose ($fh);
# Cookie登録
if ($this->c['COOKIE']) {
$this->setbbscookie();
if ($this->c['ALLOW_UNDO']) {
$this->setundocookie($message['POSTID'], $message['PCODE']);
}
}
# 過去ログ出力
if ($this->c['OLDLOGFILEDIR']) {
$dir = $this->c['OLDLOGFILEDIR'];
if ($this->c['OLDLOGFMT']) {
$oldlogext = 'dat';
}
else {
$oldlogext = 'html';
}
if ($this->c['OLDLOGSAVESW']) {
$oldlogfilename = $dir . date("Ym", CURRENT_TIME) . ".$oldlogext";
$oldlogtitle = $this->c['BBSTITLE'] . date(" Y.m", CURRENT_TIME);
}
else {
$oldlogfilename = $dir . date("Ymd", CURRENT_TIME) . ".$oldlogext";
$oldlogtitle = $this->c['BBSTITLE'] . date(" Y.m.d", CURRENT_TIME);
}
if (@filesize($oldlogfilename) > $this->c['MAXOLDLOGSIZE']) {
$this->prterror ( '過去ログファイルがサイズ制限を超えています' );
}
$fh = @fopen($oldlogfilename, "ab");
if (!$fh) {
$this->prterror ( '過去ログ出力に失敗しました' );
}
flock ($fh, 2);
$isnewdate = FALSE;
if (!@filesize($oldlogfilename)) {
$isnewdate = TRUE;
}
if ($this->c['OLDLOGFMT']) {
fwrite ($fh, $msgdata);
}
else {
# HTML出力用HTMLヘッダ
if ($isnewdate) {
$oldloghtmlhead = $this->prthtmlhead($oldlogtitle);
$oldloghtmlhead .= "$oldlogtitle\n\n
\n";
fwrite ($fh, $oldloghtmlhead);
}
$msghtml = $this->prtmessage($this->getmessage($msgdata), 3);
fwrite ($fh, $msghtml);
}
flock ($fh, 3);
fclose ($fh);
if (@filesize($oldlogfilename) > $this->c['MAXOLDLOGSIZE']) {
@chmod ($oldlogfilename, 0400);
}
# 古いログファイルを削除
if (!$this->c['OLDLOGSAVESW'] and $isnewdate) {
$limitdate = CURRENT_TIME - $this->c['OLDLOGSAVEDAY'] * 60 * 60 * 24;
$limitdate = date("Ymd", $limitdate);
$dh = opendir($dir);
while ($entry = readdir($dh)) {
$matches = array();
if (is_file($dir . $entry)
and preg_match("/(\d+)\.$oldlogext$/", $entry, $matches)) {
$timestamp = $matches[1];
if (strlen($timestamp) == strlen($limitdate) and $timestamp < $limitdate) {
unlink ($dir . $entry);
}
}
}
closedir ($dh);
}
# アーカイブの作成
if ($this->c['ZIPDIR'] and @function_exists('gzcompress')) {
# datの場合、Zipに保存するためのテンポラリファイルとしてHTML形式の過去ログ書き込みも行う
if ($this->c['OLDLOGFMT']) {
if ($this->c['OLDLOGSAVESW']) {
$tmplogfilename = $this->c['ZIPDIR'] . date("Ym", CURRENT_TIME) . ".html";
}
else {
$tmplogfilename = $this->c['ZIPDIR'] . date("Ymd", CURRENT_TIME) . ".html";
}
$fhtmp = @fopen($tmplogfilename, "ab");
if (!$fhtmp) {
return;
}
flock ($fhtmp, 2);
if (!@filesize($tmplogfilename)) {
$oldloghtmlhead = $this->prthtmlhead($oldlogtitle);
$oldloghtmlhead .= "$oldlogtitle\n\n
\n";
fwrite ($fhtmp, $oldloghtmlhead);
}
$msghtml = $this->prtmessage($this->getmessage($msgdata), 3);
fwrite ($fhtmp, $msghtml);
flock ($fhtmp, 3);
fclose ($fhtmp);
}
$tmpdir = $dir;
if ($this->c['OLDLOGFMT']) {
$tmpdir = $this->c['ZIPDIR'];
}
if ($this->c['OLDLOGSAVESW']) {
$currentfile = date("Ym", CURRENT_TIME) . ".html";
}
else {
$currentfile = date("Ymd", CURRENT_TIME) . ".html";
}
$files = array();
$dh = opendir($tmpdir);
if (!$dh) {
return;
}
while ($entry = readdir($dh)) {
if ($entry != $currentfile and is_file($tmpdir . $entry) and preg_match("/^\d+\.html$/", $entry)) {
$files[] = $entry;
}
}
closedir ($dh);
# 現ログ以外で更新時間が最新のファイル
$maxftime = 0;
foreach ($files as $filename) {
$fstat = stat ($tmpdir . $filename);
if ($fstat[9] > $maxftime) {
$maxftime = $fstat[9];
$checkedfile = $tmpdir . $filename;
}
}
if (!$checkedfile) {
return;
}
$zipfilename = preg_replace("/\.\w+$/", ".zip", $checkedfile);
# Zipファイルを作成
require_once(LIB_PHPZIP);
$zip = new PHPZip();
$zipfiles[] = $checkedfile;
$zip->Zip($zipfiles, $zipfilename);
# テンポラリファイルの削除
if ($this->c['OLDLOGFMT']) {
unlink ($checkedfile);
}
}
}
}
return 0;
}
/**
* 環境変数取得
*/
function setuserenv() {
if ($this->c['UAREC']) {
$agent = $_SERVER['HTTP_USER_AGENT'];
$agent = Func::html_escape($agent);
$this->s['AGENT'] = $agent;
}
if (!$this->c['IPREC']) {
return;
}
list ($addr, $host, $proxyflg, $realaddr, $realhost) = Func::getuserenv();
$this->s['ADDR'] = $addr;
$this->s['HOST'] = $host;
$this->s['PROXYFLG'] = $proxyflg;
$this->s['REALADDR'] = $realaddr;
$this->s['REALHOST'] = $realhost;
}
/**
* 掲示板Cookie登録
*/
function setbbscookie() {
$cookiestr = "u=" . urlencode($this->f['u']);
$cookiestr .= "&i=" . urlencode($this->f['i']);
$cookiestr .= "&c=" . $this->f['c'];
setcookie('c', $cookiestr, CURRENT_TIME + 7776000); // expires in 90 days
}
/**
* 投稿UNDO用Cookie登録
*/
function setundocookie($undoid, $pcode) {
$undokey = substr (preg_replace("/\W/", "", crypt($pcode, $this->c['ADMINPOST'])), -8);
$cookiestr = "p=$undoid&k=$undokey";
$this->s['UNDO_P'] = $undoid;
$this->s['UNDO_K'] = $undokey;
setcookie('undo', $cookiestr, CURRENT_TIME + 86400); // expires in 24 hours
}
/**
* こわれにくいカウンター処理
*
* @access public
* @param Integer こわれにくさレベル
* @return String カウンター数値
*/
function counter($countlevel = 0) {
if (!$countlevel) {
if (isset($this->c['COUNTLEVEL'])) {
$countlevel = $this->c['COUNTLEVEL'];
}
if ($countlevel < 1) {
$countlevel = 1;
}
}
$count = array();
for ($i = 0; $i < $countlevel; $i++) {
$filename = "{$this->c['COUNTFILE']}{$i}.dat";
if (is_writable ($filename) and $fh = @fopen ($filename, "r")) {
$count[$i] = fgets ($fh, 10);
fclose ($fh);
}
else {
$count[$i] = 0;
}
$filenumber[$count[$i]] = $i;
}
sort ($count, SORT_NUMERIC);
$mincount = $count[0];
$maxcount = $count[$countlevel-1] + 1;
if ($fh = @fopen("{$this->c['COUNTFILE']}{$filenumber[$mincount]}.dat", "w")) {
fputs ($fh, $maxcount);
fclose ($fh);
return $maxcount;
} else {
return 'カウンターエラー';
}
}
/**
* 参加者カウント
*
* @access public
* @param $cntfilename 記録ファイル名
* @return String 参加者数
*/
function mbrcount($cntfilename = "") {
if (!$cntfilename) {
$cntfilename = $this->c['CNTFILENAME'];
}
if ($cntfilename) {
$mbrcount = 0;
$remoteaddr = '0.0.0.0';
if ($_SERVER['REMOTE_ADDR']) {
$remoteaddr = $_SERVER['REMOTE_ADDR'];
}
$addrdec = @explode ('.', $remoteaddr);
$ukey = @array_sum($addrdec)
* @bindec(@decbin($addrdec[0]) ^ @decbin($addrdec[1]) & @decbin($addrdec[2]) ^ @decbin($addrdec[3]));
$newcntdata = array();
if (is_writable ($cntfilename)) {
$cntdata = file ($cntfilename);
$cadd = 0;
foreach ($cntdata as $cntvalue) {
if (strrpos($cntvalue, ',') !== FALSE) {
list ($cuser, $ctime,) = @explode (',', trim ($cntvalue));
if ($cuser == $ukey) {
$newcntdata[] = "$ukey,".CURRENT_TIME."\n";
$cadd = 1;
$mbrcount++;
}
else if (($ctime + $this->c['CNTLIMIT']) >= CURRENT_TIME) {
$newcntdata[] = "$cuser,$ctime\n";
$mbrcount++;
}
}
}
if (!$cadd) {
$newcntdata[] = "$ukey,".CURRENT_TIME."\n";
$mbrcount++;
}
}
else {
$newcntdata[] = "$ukey,".CURRENT_TIME."\n";
$mbrcount++;
}
if ($fh = @fopen ($cntfilename, "w")) {
$cntdatastr = implode('', $newcntdata);
flock ($fh, 2);
fwrite ($fh, $cntdatastr);
flock ($fh, 3);
fclose ($fh);
}
else {
return ('参加者ファイル出力エラー');
}
return $mbrcount;
}
else {
return;
}
}
}
/* end of class Bbs */
/**
* 共用関数クラス
*
* 設定情報に依存しない、汎用的な関数を格納したクラスです。
*
* @package strangeworld.cnscript
* @access public
*/
class Func {
/**
* コンストラクタ
*
*/
function Func() {
}
function getuserenv() {
$addr = $_SERVER['REMOTE_ADDR'];
$host = $_SERVER['REMOTE_HOST'];
$agent = $_SERVER['HTTP_USER_AGENT'];
if ($addr == $host or !$host) {
$host = gethostbyaddr ($addr);
}
$proxyflg = 0;
if ($_SERVER['HTTP_CACHE_CONTROL']) { $proxyflg = 1; }
if ($_SERVER['HTTP_CACHE_INFO']) { $proxyflg += 2; }
if ($_SERVER['HTTP_CLIENT_IP']) { $proxyflg += 4; }
if ($_SERVER['HTTP_FORWARDED']) { $proxyflg += 8; }
if ($_SERVER['HTTP_FROM']) { $proxyflg += 16; }
if ($_SERVER['HTTP_PROXY_AUTHORIZATION']) { $proxyflg += 32; }
if ($_SERVER['HTTP_PROXY_CONNECTION']) { $proxyflg += 64; }
if ($_SERVER['HTTP_SP_HOST']) { $proxyflg += 128; }
if ($_SERVER['HTTP_VIA']) { $proxyflg += 256; }
if ($_SERVER['HTTP_X_FORWARDED_FOR']) { $proxyflg += 512; }
if ($_SERVER['HTTP_X_LOCKING']) { $proxyflg += 1024; }
if (preg_match ("/cache|delegate|gateway|httpd|proxy|squid|www|via/i", $agent)) {
$proxyflg += 2048;
}
if (preg_match ("/cache|^dns|dummy|^ns|firewall|gate|keep|mail|^news|pop|proxy|smtp|w3|^web|www/i", $host)) {
$proxyflg += 4096;
}
if ($host == $addr) {
$proxyflg += 8192;
}
$realaddr = '';
$realhost = '';
if ( $proxyflg > 0 ) {
$matches = array();
if (preg_match ("/^(\d+)\.(\d+)\.(\d+)\.(\d+)/", $_SERVER['HTTP_X_FORWARDED_FOR'], $matches)) {
$realaddr = "{$matches[1]}.{$matches[2]}.{$matches[3]}.{$matches[4]}";
}
else if (preg_match ("/(\d+)\.(\d+)\.(\d+)\.(\d+)/", $_SERVER['HTTP_FORWARDED'], $matches)) {
$realaddr = "{$matches[1]}.{$matches[2]}.{$matches[3]}.{$matches[4]}";
}
else if (preg_match ("/(\d+)\.(\d+)\.(\d+)\.(\d+)/", $_SERVER['HTTP_VIA'], $matches)) {
$realaddr = "{$matches[1]}.{$matches[2]}.{$matches[3]}.{$matches[4]}";
}
else if (preg_match ("/(\d+)\.(\d+)\.(\d+)\.(\d+)/", $_SERVER['HTTP_CLIENT_IP'], $matches)) {
$realaddr = "{$matches[1]}.{$matches[2]}.{$matches[3]}.{$matches[4]}";
}
else if (preg_match ("/(\d+)\.(\d+)\.(\d+)\.(\d+)/", $_SERVER['HTTP_SP_HOST'], $matches)) {
$realaddr = "{$matches[1]}.{$matches[2]}.{$matches[3]}.{$matches[4]}";
}
else if (preg_match ("/.*\sfor\s(.+)/", $_SERVER['HTTP_FORWARDED'], $matches)) {
$realhost = $matches[1];
}
else if (preg_match ("/\-\@(.+)/", $_SERVER['HTTP_FROM'], $matches)) {
$realhost = $matches[1];
}
if (!$realaddr and $realhost) {
$realaddr = gethostbyname ($realhost);
}
}
return array($addr, $host, $proxyflg, $realaddr, $realhost);
}
/**
* プロテクトコード生成
*
* @access public
* @param Integer $timestamp タイムスタンプ
* @param Boolean $limithost 同一ホストチェックをするかどうか
* @return String プロテクトコード(英数12文字)
*/
function pcode($timestamp = 0, $limithost = TRUE) {
if (!$timestamp) {
$timestamp = CURRENT_TIME;
}
$ukey = 0;
if ($limithost) {
$remoteaddr = '0.0.0.0';
if ($_SERVER['REMOTE_ADDR']) {
$remoteaddr = $_SERVER['REMOTE_ADDR'];
}
$addrdec = @explode ('.', $remoteaddr);
$ukey = @array_sum($addrdec)
* @bindec(@decbin($addrdec[0]) ^ @decbin($addrdec[1]) & @decbin($addrdec[2]) ^ @decbin($addrdec[3]));
}
$basecode = dechex ($timestamp + $ukey);
$cryptcode = crypt ($basecode . substr($this->c['ADMINPOST'], -4), substr($this->c['ADMINPOST'], -4) . $basecode);
$cryptcode = substr (preg_replace ("/\W/", "", $cryptcode), -4);
$pcode = dechex ($timestamp) . $cryptcode;
return $pcode;
}
/**
* プロテクトコード照合
*
* @access public
* @param String $pcode プロテクトコード(英数12文字)
* @param Boolean $limithost 同一ホストチェックをするかどうか
* @return Integer タイムスタンプ
*/
function pcode_verify($pcode, $limithost = TRUE) {
if (strlen($pcode) != 12) {
return;
}
$timestamphex = substr($pcode, 0, 8);
$cryptcode = substr($pcode, 8, 4);
$ukey = 0;
if ($limithost) {
$remoteaddr = '0.0.0.0';
if ($_SERVER['REMOTE_ADDR']) {
$remoteaddr = $_SERVER['REMOTE_ADDR'];
}
$addrdec = @explode ('.', $remoteaddr);
$ukey = @array_sum($addrdec)
* @bindec(@decbin($addrdec[0]) ^ @decbin($addrdec[1]) & @decbin($addrdec[2]) ^ @decbin($addrdec[3]));
}
$timestamp = hexdec ($timestamphex);
$basecode = dechex ($timestamp + $ukey);
$verifycode = crypt ($basecode . substr($this->c['ADMINPOST'], -4), substr($this->c['ADMINPOST'], -4) . $basecode);
$verifycode = substr (preg_replace ("/\W/", "", $verifycode), -4);
if ($cryptcode != $verifycode) {
return;
}
return $timestamp;
}
/**
* チェックボックス用 フラグ出力処理
*
* @access public
* @param Integer $flag チェックボックスフラグ
* @return String チェックボックス用文字列
*/
function chkval($flag = 0, $attrvalue = FALSE) {
if ($flag) {
if ($attrvalue) {
return 'checked';
}
else {
return ' checked="checked"';
}
}
}
/**
* HTML表示用エスケープ
*
* @access public
* @param String $value 元の文字列
* @return String エスケープ処理後文字列
*/
function html_escape($value) {
if ($value == '') {
return $value;
}
#$value = strtr($value, "+", " ");
if (get_magic_quotes_gpc()) {
$value = stripslashes($value);
}
# 一時的にEUCに変換して文字化けを回避
if (!preg_match("/^\w+$/", $value)) {
#$value_euc = JcodeConvert($value, 2, 1);
#$value_euc = htmlentities($value_euc, ENT_QUOTES, 'EUC-JP');
#$value = JcodeConvert($value_euc, 1, 2);
$value = htmlspecialchars($value, ENT_QUOTES);
}
$value = str_replace("\015\012", "\015", $value);
$value = str_replace("\012", "\015", $value);
$value = str_replace("\015$", "", $value);
$value = str_replace(",", ",", $value);
#$value = str_replace("\\"", """, $value);
#$value = stripslashes($value);
return $value;
}
/**
* HTML表示用エスケープ解除
*
* @access public
* @param String $value 元の文字列
* @return String エスケープ解除処理後文字列
*/
function html_decode($value) {
if ($value == '') {
return $value;
}
# 一時的にEUCに変換して文字化けを回避
if (!preg_match("/^\w+$/", $value)) {
#$value_euc = JcodeConvert($value, 2, 1);
#$value_euc = strtr($value_euc, array_flip(get_html_translation_table(HTML_ENTITIES)));
#$value_euc = preg_replace("/([0-9]+);/me", "chr('\\1')", $value_euc);
#$value = JcodeConvert($value_euc, 1, 2);
$value = strtr($value, array_flip(get_html_translation_table(HTML_ENTITIES)));
$value = preg_replace("/([0-9]+);/me", "chr('\\1')", $value);
}
return $value;
}
/**
* 時刻フォーマット変換
*
* @access public
* @param Integer $timestamp タイムスタンプ
* @return String 日付文字列
*/
function getdatestr($timestamp, $format = "") {
if (!$format) {
$format = "Y/m/d(-) H:i:s";
}
$datestr = date($format, $timestamp);
if (strrpos($format, '-') !== FALSE) {
if (!isset($wdays)) {
static $wdays = array('日', '月', '火', '水', '木', '金', '土');
}
$datestr = str_replace('-', $wdays[date("w", $timestamp)], $datestr);
}
return $datestr;
}
/**
* 数値文字の整形
*
* @access public
* @param Integer $numberstr 元の文字列
* @return String 整形後文字列
*/
function fixnumberstr($numberstr) {
$numberstr = trim($numberstr);
$twobytenumstr = array ('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', );
for ($i = 0; $i < count($twobytenumstr); $i++) {
$numberstr = str_replace($twobytenumstr[$i], "$i", $numberstr);
}
if (is_numeric ($numberstr)) {
return $numberstr;
}
else {
return FALSE;
}
}
/**
* リンク文字列のエスケープ
*
* XSS脆弱性への対応処理です
*
* @access public
* @param Integer $numberstr 元の文字列
* @return String エスケープ処理後文字列
*/
function escape_url($src_url) {
$src_url = preg_replace("/script:/i", "script", $src_url);
$src_url = urlencode($src_url);
$src_url = str_replace ("%2F", "/", $src_url);
$src_url = str_replace ("%3A", ":", $src_url);
$src_url = str_replace ("%3D", "=", $src_url);
$src_url = str_replace ("%23", "#", $src_url);
$src_url = str_replace ("%26", "&", $src_url);
$src_url = str_replace ("%3B", ";", $src_url);
$src_url = str_replace ("%3F", "?", $src_url);
$src_url = str_replace ("%25", "%", $src_url);
return $src_url;
}
/**
* 画像タグをリンクに変換
*
* @access public
* @param String $value 元の文字列
* @return String タグ変換後文字列
*/
function conv_imgtag ($value) {
if ($value == '') {
return $value;
}
while (preg_match("/(]+>)]+)>(<\/a>)/i", $value, $matches)) {
if (preg_match("/alt=\"([^\"]+)\"/", $matches[2], $submatches)) {
$altvalue = $submatches[1];
}
else if (preg_match("/src=\"([^\"]+)\"/", $matches[2], $submatches)) {
$altvalue = substr($submatches[1], strrpos($submatches[1], '/'));
}
$value = str_replace($matches[0], " [{$matches[1]}{$altvalue}{$matches[3]}] ", $value);
}
return $value;
}
/**
* 16進文字列6文字をbase64エンコード
*
* @access public
* @param String $inputhex 16進文字列6文字
* @return String base64文字列4文字
*/
function threebytehex_base64($inputhex) {
$inputdec = hexdec($inputhex);
$a = floor($inputdec / 262144);
$tmp_a = $inputdec - 262144 * $a;
$b = floor($tmp_a / 4096);
$tmp_b = $tmp_a - 4096 * $b;
$c = floor($tmp_b / 64);
$d = $tmp_b - 64 * $c;
$basestr = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_';
$base64val = $basestr[$a] . $basestr[$b] . $basestr[$c] . $basestr[$d];
return $base64val;
}
/**
* 16進文字列6文字をbase64デコード
*
* @access public
* @param String $str base64文字列4文字
* @return String 16進文字列6文字
*/
function base64_threebytehex($str) {
if (strlen($str) != 4) {
return '';
}
$basestr = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_';
$decval =
262144 * @strrpos($basestr, substr($str, 0, 1))
+ 4096 * @strrpos($basestr, substr($str, 1, 1))
+ 64 * @strrpos($basestr, substr($str, 2, 1))
+ @strrpos($basestr, substr($str, 3, 1));
$hexval = str_pad(@dechex($decval), 6, "0", STR_PAD_LEFT);
return $hexval;
}
/**
* microtime()間の時差を測定
*
* @access public
* @param String $a 測定開始時間のmicrotime()文字列
* @param String $b 測定終了時間のmicrotime()文字列
* @return String 時差文字列
*/
function microtime_diff($a, $b) {
list($a_dec, $a_sec) = explode(" ", $a);
list($b_dec, $b_sec) = explode(" ", $b);
return $b_sec - $a_sec + $b_dec - $a_dec;
}
/**
* ファイルから行を取得
*
* @access public
* @param Integer $fh ファイルポインタ
* @param Integer $maxbuffersize 読み込みバッファサイズ
* @return String 行文字列
*/
function fgetline(&$fh, $maxbuffersize = 16000) {
$line = '';
do {
$line .= fgets($fh, $maxbuffersize);
} while (strrpos($line, "\n") === FALSE and !feof($fh));
return strlen ($line) == 0 ? FALSE : $line;
}
/**
* 携帯電話等機種判定
* @param Boolean $checkaddr IPアドレス帯域チェック有無
*/
function get_uatype($checkaddr = TRUE) {
$agent = $_SERVER['HTTP_USER_AGENT'];
$addr = $_SERVER['REMOTE_ADDR'];
if (!$agent) {
return;
}
/* iモード
IPアドレス帯域
http://docomo-web.nttdocomo.co.jp/mc-user/i/ip.html
210.153.84.0/24
210.136.161.0/24
203.138.45.0/24
User-Agent
DoCoMo/1.0/D501i
DoCoMo/1.0/SO212i/c10/TB
DoCoMo/2.0 N2051(c100;TB)
*/
if (preg_match("/^DoCoMo\//", $agent)) {
if ($checkaddr) {
foreach (array(
'210.153.84.0/24',
'210.136.161.0/24',
'203.138.45.0/24',
) as $cidraddr) {
if (Func::checkiprange($cidraddr, $addr)) {
return 'i';
}
}
return;
}
else {
return 'i';
}
}
/* EZweb
IPアドレス帯域
http://www.au.kddi.com/ezfactory/tec/spec/ezsava_ip.html
210.169.40.0/24
210.196.3.192/26
210.196.5.192/26
210.230.128.0/24
210.230.141.192/26
210.234.105.32/29
210.234.108.64/26
210.251.1.192/26
210.251.2.0/27
211.5.1.0/24
211.5.2.128/25
211.5.7.0/24
218.222.1.0/24
61.117.0.0/24
61.117.1.0/24
61.117.2.0/26
61.202.3.0/24
User-Agent
http://www.au.kddi.com/ezfactory/tec/spec/4_4.html
KDDI-HI21 UP.Browser/6.0.2.254(GUI) MMP/1.1
UP.Browser/3.01-HI02 UP.Link/3.2.1.2
*/
else if (preg_match("/UP.Browser\//", $agent)) {
if ($checkaddr) {
foreach (array(
'210.169.40.0/24',
'210.196.3.192/26',
'210.196.5.192/26',
'210.230.128.0/24',
'210.230.141.192/26',
'210.234.105.32/29',
'210.234.108.64/26',
'210.251.1.192/26',
'210.251.2.0/27',
'211.5.1.0/24',
'211.5.2.128/25',
'211.5.7.0/24',
'218.222.1.0/24',
'61.117.0.0/24',
'61.117.1.0/24',
'61.117.2.0/26',
'61.202.3.0/24',
) as $cidraddr) {
if (Func::checkiprange($cidraddr, $addr)) {
if (preg_match("/^KDDI-/", $agent)) {
return 'i';
}
else {
return 'h';
}
}
}
return;
}
else {
if (preg_match("/^KDDI-/", $agent)) {
return 'i';
}
else {
return 'h';
}
}
}
}
/**
* 指定のIPアドレス帯域にIPアドレスがあるかチェックする
* @param String $cidraddr CIDR形式のIPアドレス帯域(例: 210.153.84.0/24)
* @param String $checkaddr チェックするIPアドレス(例: 210.153.84.7)
* @return Boolean 結果
*/
function checkiprange($cidraddr, $checkaddr) {
list($netaddr, $cidrmask) = explode("/", $cidraddr);
$netaddr_long = ip2long($netaddr);
$cidrmask = pow(2, 32 - $cidrmask) - 1;
$bits1 = str_pad(decbin($netaddr_long), 32, "0", "STR_PAD_LEFT");
$bits2 = str_pad(decbin($cidrmask), 32, "0", "STR_PAD_LEFT");
$final = '';
for ($i = 0; $i < 32; $i++) {
if ($bits1[$i] == $bits2[$i]) {
$final .= $bits1[$i];
}
if ($bits1[$i] == 1 and $bits2[$i] == 0) {
$final .= $bits1[$i];
}
if ($bits1[$i] == 0 and $bits2[$i] == 1) {
$final .= $bits2[$i];
}
}
$final_long = ip2long(long2ip(bindec($final)));
$checkaddr_long = ip2long($checkaddr);
if ($checkaddr_long >= $netaddr_long and $checkaddr_long <= $final_long) {
return TRUE;
}
else {
return FALSE;
}
}
/**
* ホスト名のパターンリストマッチ
*
* @access public
* @param Array $hostlist ホスト名のパターンリスト
* @return Boolean マッチ有無
*/
function hostname_match($hostlist,$hostagent) {
if (!$hostlist or !is_array($hostlist)) {
return;
}
$hit = FALSE;
list ($addr, $host, $proxyflg, $realaddr, $realhost) = Func::getuserenv();
$agent = $_SERVER['HTTP_USER_AGENT'];
foreach ($hostlist as $hostpattern) {
foreach ($hostagent as $hostagentpattern) {
if ((preg_match("/$hostpattern/", $host) or preg_match("/$hostpattern/", $realhost)) and preg_match("/$hostagentpattern/", $agent)) {
$hit = TRUE;
break;
}
}
}
return $hit;
}
/**
* デバッグ用
*
*/
function debugwrite($debugstr, $printdate = TRUE, $debugfile = "debug.txt") {
$fhdebug = @fopen($debugfile, "ab");
if (!$fhdebug) {
return;
}
flock ($fhdebug, 2);
if ($printdate) {
fwrite ($fhdebug, date("Y/m/d H:i:s\t (T)", CURRENT_TIME));
}
fwrite ($fhdebug, "$debugstr\n");
flock ($fhdebug, 3);
fclose ($fhdebug);
}
}
/* end of class Func */
?>