<?php
/**
* [PHPFOX_HEADER]
*/
defined('PHPFOX') or exit('NO DICE!');
/**
*
*
* @copyright [PHPFOX_COPYRIGHT]
* @author Raymond Benc
* @package Phpfox_Service
* @version $Id: ban.class.php 7029 2014-01-08 14:30:56Z Fern $
*/
class Ban_Service_Ban extends Phpfox_Service
{
/**
* Class constructor
*/
public function __construct()
{
$this->_sTable = Phpfox::getT('ban');
}
/**
* @param string $sType
*
* @return array
*/
public function getFilters($sType)
{
$sCacheId = $this->cache()->set('ban_type_' . $sType);
if (!$aFilters = $this->cache()->get($sCacheId)) {
$aFilters = $this->database()
->select('b.*, ' . Phpfox::getUserField())
->from($this->_sTable, 'b')
->leftJoin(Phpfox::getT('user'), 'u', 'u.user_id = b.user_id')
->where('b.type_id = \'' . $this->database()->escape($sType) . '\'')
->execute('getSlaveRows');
foreach ($aFilters as $iKey => $aFilter) {
if (!empty($aFilter['user_groups_affected'])) {
$aUserGroups = unserialize($aFilter['user_groups_affected']);
$aFilters[$iKey]['user_groups_affected'] = [];
$sWhere = '';
foreach ($aUserGroups as $iUserGroup) {
$sWhere .= 'user_group_id = ' . $iUserGroup . ' OR ';
}
$sWhere = rtrim($sWhere, ' OR ');
$aFilters[$iKey]['user_groups_affected'] = User_Service_Group_Group::instance()->get($sWhere);
}
}
$this->cache()->save($sCacheId, $aFilters);
}
return $aFilters;
}
/**
* @param string $sType
* @param string $sValue
*
* @return bool
*/
public function check($sType, $sValue)
{
$sCacheId = $this->cache()->set('ban_' . $sType);
if (!($aFilters = $this->cache()->get($sCacheId))) {
$aRows = $this->database()->select('find_value')
->from($this->_sTable)
->where('type_id = \'' . $this->database()->escape($sType) . '\'')
->execute('getSlaveRows');
foreach ($aRows as $aRow) {
$aFilters[trim($aRow['find_value'])] = true;
}
$this->cache()->save($sCacheId, $aFilters);
}
if ($sType == 'display_name') {
$sValue = $this->preParse()->convert($sValue);
}
if (is_array($aFilters) && count($aFilters)) {
foreach ($aFilters as $sFilter => $mValue) {
$sFilter = str_replace('*', '*', $sFilter);
if ($sType == 'ip') {
$sFilter = preg_replace('%[^0-9.*]%', '', $sFilter);
if ($sFilter == '*') {
continue;
}
}
if (preg_match('/\*/i', $sFilter)) {
$sFilter = str_replace(array('.', '*'), array('\.', '(.*?)'), $sFilter);
if(preg_match('/http(s?):\/\//i', $sFilter)) {
$sFilter = str_replace('/', '\\/\\/', $sFilter);
}
if (preg_match('/^' . $sFilter . '$/i', $sValue)) {
return false;
}
} else {
if (preg_match('/^' . $sFilter . '$/i', $sValue, $aMatches)) {
return false;
}
}
}
}
return true;
}
/**
* This function resembles $this->check but it also handles banning and is a more direct approach
* and handles redirection and db insertion
* This function is called in every Service as opposed to a Library mainly because there may be
* cases where it becomes too restrictive
* If the user groups affected is an empty array, it assumes that it affects every user group.
* This function has been implemented in the following services
* - Blog.process (Add, update, updateBlogText, updateBlogTitle)
* - Comment.process (Add, updateText)
* - Event.process (Add, massEmail, update)
* - Forum.post.process (Add, update, updateText)
* - Forum.thread.process (Add, update)
* - Group.process (Add, update)
* - Mail.process (Add)
* - Marketplace.process (Add, update)
* - Music.process (upload)
* - Music.album.process (add, update)
* - Music.genre.process (add, update)
* - Music.song.process (setName)
* - Newsletter.process (add)
* - Page.process (add)
* - Photo.process (add)
* - Photo.album.process (add, updateTitle)
* - Photo.category.process (add)
* - Photo.tag.process (add)
* - Poll.process (add, updateAnswer)
* - Quiz.process (add, update)
* - User.process (updateStatus:2.1.0 RC1)
* @param string $sValue
* @return bool false on fail. In some situations it does'nt help echo'ing here (comment)
*/
public function checkAutomaticBan($sValue)
{
/* Extra protection for admins so they don't get banned automatically. */
if (Phpfox::isAdmin() || empty($sValue)) {
return true;
}
if (is_array($sValue)) {
$sValue = $this->_flatten($sValue);
}
$sCacheBanWord = $this->cache()->set('ban_work_filter');
if (!$aFilters = $this->cache()->get($sCacheBanWord)){
$aFilters = $this->database()->select('*')
->from($this->_sTable)
->where('type_id = "word"')
->execute('getSlaveRows');
$this->cache()->set($sCacheBanWord, $aFilters);
}
foreach ($aFilters as $iKey => $aFilter) {
$aUserGroupsAffected = unserialize($aFilter['user_groups_affected']);
if (is_array($aUserGroupsAffected) && !empty($aUserGroupsAffected) && in_array(Phpfox::getUserBy('user_group_id'), $aUserGroupsAffected) == false) {
continue;
}
$sFilter = ''.str_replace('*', '*', $aFilter['find_value']) .'';
$sFilter = str_replace("/", "\/", $sFilter);
$sFilter = str_replace('*', '*', $sFilter);
if (preg_match('/\*/i', $sFilter)) {
$sFilter = str_replace(array('.', '*'), array('\.', '(.*?)'), $sFilter);
$bBan = preg_match('/' . $sFilter . '/is', $sValue);
} else {
$bBan = preg_match("/(\W)". $sFilter ."(\W)/i", $sValue);
if (!$bBan) {
$bBan = preg_match("/^". $sFilter ."(\W)/i", $sValue);
}
if (!$bBan) {
$bBan = preg_match("/(\W)". $sFilter ."$/i", $sValue);
}
if (!$bBan) {
$bBan = preg_match("/^". $sFilter ."$/i", $sValue);
}
}
if ($bBan) {
if ($aFilter['days_banned'] === null) {
return true;
}
$this->database()->insert(Phpfox::getT('ban_data'), array(
'ban_id' => $aFilter['ban_id'],
'user_id' => Phpfox::getUserId(),
'start_time_stamp' => PHPFOX_TIME,
'end_time_stamp' => $aFilter['days_banned'] > 0 ? PHPFOX_TIME + ($aFilter['days_banned'] * 86400) : 0,
'return_user_group' => $aFilter['return_user_group'],
'reason' => $aFilter['reason']
));
define('PHPFOX_USER_IS_BANNED', true);
$aFilter['reason'] = str_replace(''', "'", $aFilter['reason']);
$sReason = preg_replace_callback('/\{phrase var=\'(.*)\'\}/is', function($m) {
return "'' . _p('{$m[1]}',array(), '" . Phpfox::getUserBy('language_id') . "') . ''";
}, $aFilter['reason']);
$iUserGroupId = Phpfox::getParam('core.banned_user_group_id');
if ($iUserGroupId == 0) {
$iUserGroupId = 5;
}
$this->database()->update(Phpfox::getT('user'), [
'user_group_id' => $iUserGroupId]
, 'user_id = ' . (int)Phpfox::getUserId());
User_Service_Auth::instance()->logout();
if (defined('PHPFOX_IS_AJAX') && PHPFOX_IS_AJAX) {
echo 'alert("' . $sReason . '");';
echo 'window.location.reload(true);';
} else {
Phpfox_Url::instance()->send('', array(), $sReason);
}
return false;
}
}
return true;
}
/**
* Simple function to recursively array_values. Used only with checkAutomaticBan
* @param array|string $aArr
* @return string
*/
private function _flatten($aArr)
{
if (!is_array($aArr)) {
return $aArr;
}
$sStr = '';
foreach ($aArr as $aA) {
$sStr .= $this->_flatten($aA) . ' ';
}
return $sStr;
}
/**
* This function checks if $iUser is banned taking into account the user_group_id index and the ban_data table
* @param array $aUser
* @return array is_banned => bool, undefined|reason:string
*/
public function isUserBanned($aUser = array())
{
$sCacheBanned = $this->cache()->set('ban_user_banned_' . md5(serialize($aUser)));
if (!$aBanned = $this->cache()->get($sCacheBanned)) {
$aBanned = $this->database()->select('*')
->from(Phpfox::getT('ban_data'))
->where('user_id = ' . ((!isset($aUser['user_id']) || $aUser['user_id'] == null) ? Phpfox::getUserId() : (int)$aUser['user_id']) . ' AND is_expired = 0')
->execute('getSlaveRow');
$this->cache()->save($sCacheBanned, $aBanned);
}
/* Users banned in version 2.0 do not have a record in ban_data but belong to the banned user group */
if (!isset($aBanned['user_id']) &&
isset($aUser['user_group_id']) &&
User_Service_Group_Setting_Setting::instance()->getGroupParam($aUser['user_group_id'],'core.user_is_banned')) {
return array('is_banned' => true);
}
/* Users banned in version 2.1 do have a record in ban_data where is_expired == 0 and the time stamp is
either 0 or in the future */
if (isset($aBanned['is_expired']) && $aBanned['is_expired'] == 0 && isset($aBanned['end_time_stamp'])
&& ($aBanned['end_time_stamp'] == 0 || $aBanned['end_time_stamp'] > PHPFOX_TIME))
{
return array_merge(array('is_banned' => true), $aBanned);
}
return array_merge(array('is_banned' => false), $aBanned);
}
/**
* If a call is made to an unknown method attempt to connect
* it to a specific plug-in with the same name thus allowing
* plug-in developers the ability to extend classes.
*
* @param string $sMethod is the name of the method
* @param array $aArguments is the array of arguments of being passed
* @return null
*/
public function __call($sMethod, $aArguments)
{
/**
* Check if such a plug-in exists and if it does call it.
*/
if ($sPlugin = Phpfox_Plugin::get('ban.service_ban__call'))
{
eval($sPlugin);
return null;
}
/**
* No method or plug-in found we must throw a error.
*/
Phpfox_Error::trigger('Call to undefined method ' . __CLASS__ . '::' . $sMethod . '()', E_USER_ERROR);
}
}