* @copyright Yuji Iwashita * @version $Id: spamp.php,v 1.6 2010/03/28 16:24:07 YujiIwashita Exp YujiIwashita $ */ class spamp { /** * Configration params * * @access protected * @var array */ var $Conf = array(); /** * Error Messages * * @access private * @var array */ var $__Messages = array(); /** * Public methods: */ /** * Constructer for PHP4 * * @access public * @param string $filename Configration filename */ function spamp ( $filename = null ) { $this->_loadConf($filename); } /** * Constructer for PHP5 * * @access public * @param string $filename Configration filename */ function __construct ( $filename = null ) { $this->_loadConf($filename); } /** * Put 1x1 GIF image * * @access public * @param string $formKey default $_SERVER['HTTP_REFERER'] * @return void */ function putImage ( $formKey = null ) { if ($this->_headersSent()) { return false; } // Send Cookie if ($this->Conf['cookieValidate']) { $this->sendCookie(); } // Save session $this->saveSession($formKey); // Put GIF Image header('X-Powered-By: Spamp'); header('Cache-Control: no-cache'); header('Expires: Sat, 26 Jul 1997 05:00:00 GMT'); header('Content-Type: image/gif'); header('Content-Length: 39'); echo base64_decode('R0lGODlhAQABAGAAACH5BAEKAP8ALAAAAAABAAEAAAgEAP8FBAA7'); // Remove timeout files if (7 & time()) { $this->removeSessionAll(); } exit(); } /** * Is SPAM Request? * * @access public * @param string $formKey default $Conf['formKey'] or $_SERVER['HTTP_REFERER'] * @param string $ip default $_SERVER['REMOTE_ADDR'] * @param string $ua default $_SERVER['HTTP_USER_AGENT'] * @param string $server default $Conf['server'] or $_SERVER['HTTP_HOST'] * @return int 0:No spam, 1:Cookie, 2:Session, 4:DenyHost, 8:DenyWord */ function isSpam ( $formKey = null, $ip = null, $ua = null, $server = null ) { if (null === $ip) { $ip = $_SERVER['REMOTE_ADDR']; } // Allow hosts if ($this->isAllowHost($ip)) { return 0; } $result = 0; // cookie if ($this->Conf['cookieValidate'] && $this->isSpamCookie()) { $result = 1; } // session elseif (!$this->hasSession($formKey, $ip, $ua, $server)) { $result = 2; } // Deny hosts elseif ($this->isDenyHost($ip)) { $result = 4; } // form param elseif (isset($_POST) && count($_POST) && $this->isDenyWord(serialize($_POST))) { $result = 8; } // SPAM if ($result) { // send cookie if ($this->Conf['cookieValidate']) { $this->sendCookie(md5(microtime())); } // deny logging if ($this->Conf['denyLogging']) { $this->saveDenylog($this->Conf['denyLogFile'], $result); } } // Remove timeout files if (3 & time()) { $this->removeSessionAll(); } return $result; } /** * Has Session? * * @access public * @param string $formKey default $Conf['formKey'] or $_SERVER['HTTP_REFERER'] * @param string $ip default $_SERVER['REMOTE_ADDR'] * @param string $ua default $_SERVER['HTTP_USER_AGENT'] * @param string $server default $Conf['server'] or $_SERVER['HTTP_HOST'] * @return boolean */ function hasSession ( $formKey = null, $ip = null, $ua = null, $server = null ) { if (null === $formKey) { if (null !== $this->Conf['formKey']) { $formKey = $this->Conf['formKey']; } elseif (isset($_SERVER['HTTP_REFERER'])) { $formKey = $_SERVER['HTTP_REFERER']; } else { $formKey = ''; } } if (null === $server) { $server = $this->Conf['server']; } $sessFile = $this->_generateSessFile($formKey, $ip, $ua, $server); if (is_file($sessFile)) { $fileTime = filemtime($sessFile); if ( ($fileTime + $this->Conf['lifeTime']) > time() && ($fileTime + $this->Conf['waitingTime']) < time() ) { return true; } elseif (is_writable($sessFile)) { @unlink($sessFile); } } return false; } /** * Save Session * * @access public * @param string $formKey default $_SERVER['HTTP_REFERER'] * @param string $ip default $_SERVER['REMOTE_ADDR'] * @param string $ua default $_SERVER['HTTP_USER_AGENT'] * @param string $server default $_SERVER['HTTP_HOST'] * @return boolean */ function saveSession ( $formKey = null, $ip = null, $ua = null, $server = null ) { if (!$this->_useWorkDir()) { return false; } $sessFile = $this->_generateSessFile($formKey, $ip, $ua, $server); $fp = fopen($sessFile, 'wb'); fclose($fp); return true; } /** * Remove Session * * @access public * @param string $formKey default $_SERVER['HTTP_REFERER'] * @param string $ip default $_SERVER['REMOTE_ADDR'] * @param string $ua default $_SERVER['HTTP_USER_AGENT'] * @param string $server default $_SERVER['HTTP_HOST'] * @return boolean */ function removeSession ( $formKey = null, $ip = null, $ua = null, $server = null ) { if (!$this->_useWorkDir()) { return false; } $sessFile = $this->_generateSessFile($formKey, $ip, $ua, $server); if (!is_file($sessFile)) { return true; } return @unlink($sessFile); } /** * Remove Session files all * * @access public * @param string $all true: all, false: timeout only (default) * @return boolean */ function removeSessionAll ( $all = false ) { if (!$this->_useWorkDir()) { return false; } $sessFiles = glob($this->Conf['workDir'].DIRECTORY_SEPARATOR.'*'.$this->Conf['sessSuffix']); if (!is_array($sessFiles) || !isset($sessFiles[0])) { return true; } if ($all) { foreach ($sessFiles as $file) { @unlink($file); } } else { $limit = time() - $this->Conf['lifeTime']; foreach ($sessFiles as $file) { if ($limit > filemtime($file)) { @unlink($file); } } } return true; } /** * Is SPAM Cookie * * @access public * @access string $cookie * @return boolean */ function isSpamCookie ( $cookie = null ) { if (null === $cookie) { if (!isset($_COOKIE[$this->Conf['cookieName']])) { return false; } $cookie = $_COOKIE[$this->Conf['cookieName']]; } $params = explode('-', $cookie); if (3 !== count($params)) { return true; } $cookieTime = (int)$params[0]; if (($cookieTime + $this->Conf['waitingTime']) > time()) { return true; } if ($cookie !== $this->_generateCookie($params[0], $params[1])) { return true; } return false; } /** * Send Cookie * * @access public * @param string $spamKey SPAM only * @return boolean */ function sendCookie ( $spamKey = '' ) { if ($this->_headersSent()) { return false; } return setcookie( $this->Conf['cookieName'], $this->_generateCookie(null, null, $spamKey), time() + $this->Conf['cookieLifeTime'], $this->Conf['cookiePath'], $this->Conf['cookieDomain'] ); } /** * Remove SPAM Cookie * * @access public * @return boolean */ function removeCookie () { if ($this->_headersSent()) { return false; } if (isset($_COOKIE[$this->Conf['cookieName']])) { unset($_COOKIE[$this->Conf['cookieName']]); } return setcookie( $this->Conf['cookieName'], '', 1, $this->Conf['cookiePath'], $this->Conf['cookieDomain'] ); } /** * Is Allow host? * * @access public * @param string $ip default $_SERVER['REMOTE_ADDR'] * @return boolean */ function isAllowHost ( $ip = null ) { static $hosts = null; if (null === $hosts) { $hosts = $this->_loadData($this->Conf['confDir'].DIRECTORY_SEPARATOR.$this->Conf['allowHostsFile']); } if (!is_array($hosts) || !isset($hosts[0])) { return false; } if (null === $ip) { $ip = $_SERVER['REMOTE_ADDR']; } foreach ($hosts as $host) { if ($this->inNetwork($ip, $host)) { return true; } } return false; } /** * Is Deny host? * * @access public * @param string $ip default $_SERVER['REMOTE_ADDR'] * @return boolean */ function isDenyHost ( $ip = null ) { static $hosts = null; if (null === $hosts) { $hosts = $this->_loadData($this->Conf['confDir'].DIRECTORY_SEPARATOR.$this->Conf['denyHostsFile']); } if (!is_array($hosts) || !isset($hosts[0])) { return false; } if (null === $ip) { $ip = $_SERVER['REMOTE_ADDR']; } foreach ($hosts as $host) { if ($this->inNetwork($ip, $host)) { return true; } } return false; } /** * Is Deny Word? * * @access public * @param string $word * @return boolean */ function isDenyWord ( $word ) { static $words = null; if (!is_string($word) || !isset($word{0})) { return false; } if (null === $words) { $words = $this->_loadData($this->Conf['confDir'].DIRECTORY_SEPARATOR.$this->Conf['denyWordsFile']); } if (!is_array($words) || !isset($words[0])) { return false; } foreach ($words as $wd) { $reg = '/'.preg_quote($wd).'/i'; if (preg_match($reg, $word)) { return true; } } return false; } /** * In Network address * * @access public * @param string $ip IPv4 (000.000.000.000) * @param string $network IPv4 or CIDR (000.000.000.000/00) * @return boolean */ function inNetwork ( $ip, $network ) { if (false === strpos($network, '/')) { return ($ip === $network) ? true: false; } list($net, $mask) = explode('/', $network); $shift = 32 - $mask; $ip = ip2long($ip) >> $shift; $net = ip2long($net) >> $shift; return ($ip === $net) ? true: false; } /** * Get Request params * * @access public * @param int $addFlag 0: Basic params only (default), 1: $_SERVER['HTTP_*'], 2: $_POST, 3: $_SERVER['HTTP_*'] | $_POST * @return array */ function getRequestParams ( $addFlag = 0 ) { $params = array(); // Basic params $params['sec'] = time(); $params['server'] = $_SERVER['HTTP_HOST']; $params['method'] = $_SERVER['REQUEST_METHOD']; $params['protocol'] = $_SERVER['SERVER_PROTOCOL']; $params['uri'] = $_SERVER['REQUEST_URI']; $params['referer'] = (isset($_SERVER['HTTP_REFERER'])) ? $_SERVER['HTTP_REFERER']: ''; $params['host'] = $_SERVER['REMOTE_ADDR']; $params['user'] = (isset($_SERVER['REMOTE_USER'])) ? $_SERVER['REMOTE_USER']: ''; $params['agent'] = (isset($_SERVER['HTTP_USER_AGENT'])) ? $_SERVER['HTTP_USER_AGENT']: ''; // Add params $_SERVER['HTTP_*'] if (1 & $addFlag) { $http = array(); foreach ($_SERVER as $key => $val) { if ('H' !== $key{0} || 'HTTP_' !== substr($key, 0, 5) || 'HTTP_HOST' === $key || 'HTTP_USER_AGENT' === $key || 'HTTP_REFERER' === $key) { continue; } $http[$key] = $val; } $params['http'] = $http; } // Add params $_POST if (2 & $addFlag && isset($_POST) && count($_POST)) { $params['post'] = $_POST; } return $params; } /** * Save Deny log * * @access public * @param string $filename Log filename * @param int $result add result * @return boolean */ function saveDenylog ( $filename = null, $result = null ) { if (!is_string($filename)) { $filename = $this->Conf['workDir'].DIRECTORY_SEPARATOR.$this->Conf['denyLogFile']; } if ('/' !== $filename{0} && ':' !== $filename{1}) { $filename = $this->Conf['workDir'].DIRECTORY_SEPARATOR.$filename; } if (!is_writable(dirname($filename))) { $this->_setMessage('saveDenylog(): Not writable attribute directory. '.dirname($filename)); return false; } $record = $this->getRequestParams($this->Conf['denyLoggingParam']); if (function_exists('mb_convert_encoding')) { $inputEncoding = mb_detect_encoding(serialize($_POST)); if ('ASCII' !== $inputEncoding && $inputEncoding !== $this->Conf['internalEncoding']) { $record = $this->_convertEncoding($record, $this->Conf['internalEncoding'], $inputEncoding); } } $record = $this->_serializeEncode($record); if (is_integer($result) && 0 < $result) { $record['result'] = $result; } return error_log(serialize($record)."\n", 3, $filename); } /** * Load Deny log * * @access public * @param string $filename Log filename * @return array */ function loadDenylog ( $filename = null ) { if (!is_string($filename)) { $filename = $this->Conf['workDir'].DIRECTORY_SEPARATOR.$this->Conf['denyLogFile']; } if ('/' !== $filename{0} && ':' !== $filename{1}) { $filename = $this->Conf['workDir'].DIRECTORY_SEPARATOR.$filename; } if (!is_file($filename) || !is_readable($filename)) { return array(); } $lines = file($filename); if (!isset($lines[0])) { return array(); } $log = array(); foreach ($lines as $line) { $line = trim($line); if (!$line) { continue; } $log[] = unserialize($line); } return $log; } /** * Get Error messages * * @access public * @return array Error messages */ function getMessage () { return $this->__Messages; } /** * Get Spamp version number * * @access public * @return string */ function version () { return 'bate 5'; } /** * Protected methods: */ /** * Load Configration & Initialize * * @access protected * @param string $filename Configration filename * @return boolean */ function _loadConf ( $filename = null ) { $defaultFile = 'spamp.conf.php'; // Load Configration file if (!is_string($filename) || !$filename) { $filename = dirname(__FILE__).DIRECTORY_SEPARATOR.$defaultFile; } if ('/' !== $filename{0} && ':' !== $filename{1}) { $filename = dirname(__FILE__).DIRECTORY_SEPARATOR.$filename; } if (is_dir($filename)) { $dir = (isset($filename{1})) ? rtrim($filename, '\\/'): $filename; $filename = $dir.DIRECTORY_SEPARATOR.$defaultFile; } $this->Conf['confFile'] = $filename; $this->Conf['confDir'] = dirname($filename); if (is_file($this->Conf['confFile'])) { include $filename; } else { $this->__Messages[] = '[spamp] Don\'t loaded configration file. '.$filename; } if (!isset($workDir) || !$workDir) { $workDir = $this->Conf['confDir'].DIRECTORY_SEPARATOR.'work'; } $this->Conf['workDir'] = rtrim($workDir, '\\/'); if (!is_dir($this->Conf['workDir']) || !is_writable($this->Conf['workDir'])) { $this->__Messages[] = '[spamp] Not exists or Not writable attribute Work directory.'.$this->Conf['workDir']; } $this->Conf['signature'] = (!isset($signature) || !is_string($signature)) ? '': $signature; $this->Conf['formKey'] = (!isset($formKey) || !is_string($formKey)) ? null: $formKey; $this->Conf['server'] = (!isset($server) || !is_string($server)) ? $_SERVER['HTTP_HOST']: $server; $this->Conf['lifeTime'] = (!isset($lifeTime) || !is_numeric($lifeTime) || 0 >= $lifeTime) ? 3600: (int)$lifeTime; $this->Conf['waitingTime'] = (!isset($waitingTime) || !$waitingTime) ? 0: (int)$waitingTime; $this->Conf['allowHostsFile'] = (!isset($allowHostsFile) || !$allowHostsFile) ? 'allow.host.php': $allowHostsFile; $this->Conf['denyHostsFile'] = (!isset($denyHostsFile) || !$denyHostsFile) ? 'deny.host.php': $denyHostsFile; $this->Conf['denyWordsFile'] = (!isset($denyWordsFile) || !$denyWordsFile) ? 'deny.word.php': $denyWordsFile; $this->Conf['sessSuffix'] = (!isset($sessSuffix) || !$sessSuffix) ? '.sess': $sessSuffix; $this->Conf['cacheSuffix'] = (!isset($cacheSuffix) || !$cacheSuffix) ? '.cache': $cacheSuffix; $this->Conf['denyLogging'] = (!isset($denyLogging) || !$denyLogging) ? false: true; $this->Conf['denyLoggingParam'] = (!isset($denyLoggingParam) || !$denyLoggingParam) ? 3: (int)$denyLoggingParam; $this->Conf['denyLogFile'] = (!isset($denyLogFile) || !$denyLogFile) ? 'deny.log': $denyLogFile; $this->Conf['errorLogging'] = (!isset($errorLogging) || !$errorLogging) ? false: true; $this->Conf['errorLogFile'] = (!isset($errorLogFile) || !$errorLogFile) ? 'error.log': $errorLogFile; $this->Conf['internalEncoding'] = (!isset($internalEncoding) || !$internalEncoding) ? 'UTF-8': $internalEncoding; $this->Conf['cookieValidate'] = (!isset($cookieValidate) || !$cookieValidate) ? false: true; $this->Conf['cookieName'] = (!isset($cookieName) || !$cookieName) ? 'spamp': $cookieName; $this->Conf['cookieLifeTime'] = (!isset($cookieLifeTime) || !is_numeric($cookieLifeTime)) ? $this->Conf['lifeTime']: (int)$cookieLifeTime; $this->Conf['cookiePath'] = (!isset($cookiePath) || is_string($cookiePath) || '/' !== $cookiePath{0}) ? '/': $cookiePath; $this->Conf['cookieDomain'] = (!isset($cookieDomain) || is_string($cookieDomain)) ? $this->Conf['server']: $cookieDomain; return true; } /** * Generate Session ID * * @access protected * @param string $formKey default $_SERVER['HTTP_REFERER'] * @param string $ip default $_SERVER['REMOTE_ADDR'] * @param string $ua default $_SERVER['HTTP_USER_AGENT'] * @param string $server default $_SERVER['HTTP_HOST'] * @return string */ function _generateSessId ( $formKey = null, $ip = null, $ua = null, $server = null ) { if (null === $formKey) { $formKey = (isset($_SERVER['HTTP_REFERER'])) ? $_SERVER['HTTP_REFERER']: ''; } if (null === $ip) { $ip = $_SERVER['REMOTE_ADDR']; } if (null === $ua) { $ua = (isset($_SERVER['HTTP_USER_AGENT'])) ? $_SERVER['HTTP_USER_AGENT']: ''; } if (null === $server) { $server = $_SERVER['HTTP_HOST']; } return md5($this->Conf['signature'].'/'.$formKey.'/'.$ip.'/'.$ua.'/'.$server); } /** * Generate Session filename * * @access protected * @param string $formKey default $_SERVER['HTTP_REFERER'] * @param string $ip default $_SERVER['REMOTE_ADDR'] * @param string $ua default $_SERVER['HTTP_USER_AGENT'] * @param string $server default $_SERVER['HTTP_HOST'] * @return string Session filename */ function _generateSessFile ( $formKey = null, $ip = null, $ua = null, $server = null ) { $sessId = $this->_generateSessId($formKey, $ip, $ua, $server); return $this->Conf['workDir'].DIRECTORY_SEPARATOR.$sessId.$this->Conf['sessSuffix']; } /** * Generate Cookie * * @access public * @param int $sec timestamp default time() * @param string $host IP address default $_SERVER['REMOTE_ADDR'] * @param string $spamKey * @return string */ function _generateCookie ( $sec = null, $host = null, $spamKey = '' ) { if (null === $sec) { $sec = (string)time(); } if (null === $host) { $host = $_SERVER['REMOTE_ADDR']; } $param = $sec.'-'.$host.'-'.md5($sec.$host.$this->Conf['signature'].$spamKey); return $param; } /** * Load Data file * * @access protected * @param string $filename AllowHosts, DanyHosts, DenyWords * @return array */ function _loadData ( $filename ) { $cacheFile = $this->Conf['workDir'].DIRECTORY_SEPARATOR.md5($filename).$this->Conf['cacheSuffix']; if (is_file($cacheFile) && is_readable($cacheFile)) { if (!is_file($filename) || filemtime($cacheFile) >= filemtime($filename)) { return unserialize(trim(file_get_contents($cacheFile))); } } if (!is_file($filename) || !is_readable($filename)) { return array(); } $lines = file($filename); if (!is_array($lines) || !isset($lines[0])) { return array(); } $data = array(); foreach ($lines as $line) { $line = trim($line); if ('' === $line || '#' === $line{0}) { continue; } $data[] = $line; } if ($this->_useWorkDir()) { $fp = fopen($cacheFile, 'wb'); fwrite($fp, serialize($data)); fclose($fp); } return $data; } /** * Set message * * @access protected * @param string $message Error messege * @param int $level * @return void */ function _setMessage ( $message = '', $level = 0 ) { $this->__Messages[] = '[Spamp] '.$message; if ( $this->Conf['errorLogging'] && is_dir($this->Conf['workDir']) && is_writable($this->Conf['workDir']) && $this->Conf['errorLogFile'] ) { error_log( date('Y-m-d H:i:s', time()).' : '.$message."\n", 3, $this->Conf['workDir'].DIRECTORY_SEPARATOR.$this->Conf['errorLogFile'] ); } } /** * Work dir has Writable attribute * * @access protected * @return boolean */ function _useWorkDir () { if (!is_dir($this->Conf['workDir']) || !is_writable($this->Conf['workDir'])) { $this->_setMessage('Not exists or Not writable attribute Work directory.'.$this->Conf['workDir']); return false; } return true; } /** * Has headers sent * * @access protected * @return boolean */ function _headersSent () { if (headers_sent($file, $line)) { $this->_setMessage('headers already sent. '.$file.':'.$line); return true; } return false; } /** * Convert Encoding for Array * * @access protected * @param mix $params Strings or Array * @param string $outEnc output encoding * @param string $inEnc input encoding * @return mix */ function _convertEncoding ( $params, $outEnc, $inEnc ) { if (is_string($params)) { $params = mb_convert_encoding($params, $outEnc, $inEnc); } elseif (is_array($params) && count($params)) { foreach ($params as $key => $param) { if (is_string($param)) { $params[$key] = mb_convert_encoding($param, $outEnc, $inEnc); } elseif (is_array($param)) { $params[$key] = $this->_convertEncoding($param, $outEnc, $inEnc); } } } return $params; } /** * Serialize Encode for Arrey * * @access protected * @param mix $params Strings or Array * @return mix */ function _serializeEncode ( $params ) { if (is_string($params)) { $params = preg_replace('/[\x00-\x1f\x7f]/', ' ', $params); } elseif (is_array($params) && count($params)) { foreach ($params as $key => $param) { if (is_string($param)) { $params[$key] = preg_replace('/[\x00-\x1f\x7f]/', ' ', $param); } elseif (is_array($param)) { $params[$key] = $this->_serializeEncode($param); } } } return $params; } } /* end of spamp class */