742 lines
19 KiB
PHP
742 lines
19 KiB
PHP
<?php
|
|
/**
|
|
* MyBB 1.8
|
|
* Copyright 2014 MyBB Group, All Rights Reserved
|
|
*
|
|
* Website: http://www.mybb.com
|
|
* License: http://www.mybb.com/about/license
|
|
*
|
|
*/
|
|
|
|
// Disallow direct access to this file for security reasons
|
|
if(!defined("IN_MYBB"))
|
|
{
|
|
die("Direct initialization of this file is not allowed.<br /><br />Please make sure IN_MYBB is defined.");
|
|
}
|
|
|
|
/**
|
|
* Login handling class, provides common structure to handle login events.
|
|
*
|
|
*/
|
|
class WarningsHandler extends DataHandler
|
|
{
|
|
/**
|
|
* The language file used in the data handler.
|
|
*
|
|
* @var string
|
|
*/
|
|
public $language_file = 'datahandler_warnings';
|
|
|
|
/**
|
|
* The prefix for the language variables used in the data handler.
|
|
*
|
|
* @var string
|
|
*/
|
|
public $language_prefix = 'warnings';
|
|
|
|
/**
|
|
* The stored data for the warning being written.
|
|
*
|
|
* @var array
|
|
*/
|
|
public $write_warning_data = array();
|
|
|
|
/**
|
|
* The stored data for the warning being retrieved.
|
|
*
|
|
* @var array
|
|
*/
|
|
private $read_warning_data = array();
|
|
|
|
/**
|
|
* Friendly redirect action after inserting a new warning.
|
|
*
|
|
* @var string
|
|
*/
|
|
public $friendly_action = '';
|
|
|
|
/**
|
|
* Validate a warning user assets.
|
|
*
|
|
* @return boolean True when valid, false when invalid.
|
|
*/
|
|
function validate_user()
|
|
{
|
|
global $mybb;
|
|
|
|
$warning = &$this->data;
|
|
|
|
$user = get_user($warning['uid']);
|
|
|
|
if(!$user['uid'])
|
|
{
|
|
$this->set_error('error_invalid_user');
|
|
return false;
|
|
}
|
|
|
|
if($user['uid'] == $mybb->user['uid'])
|
|
{
|
|
$this->set_error('error_cannot_warn_self');
|
|
return false;
|
|
}
|
|
|
|
if($user['warningpoints'] >= $mybb->settings['maxwarningpoints'])
|
|
{
|
|
$this->set_error('error_user_reached_max_warning');
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Validate a warning post.
|
|
*
|
|
* @return boolean True when valid, false when invalid.
|
|
*/
|
|
function validate_post()
|
|
{
|
|
$warning = &$this->data;
|
|
|
|
$post = get_post($warning['pid']);
|
|
|
|
if(!$post['pid'])
|
|
{
|
|
$this->set_error('error_invalid_post');
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Validate a warning notes.
|
|
*
|
|
* @return boolean True when valid, false when invalid.
|
|
*/
|
|
function validate_notes()
|
|
{
|
|
$warning = &$this->data;
|
|
|
|
if(!trim($warning['notes']))
|
|
{
|
|
$this->set_error('error_no_note');
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Validate maximum warnings per day for current user.
|
|
*
|
|
* @return boolean True when valid, false when invalid.
|
|
*/
|
|
function validate_maximum()
|
|
{
|
|
global $mybb, $db, $lang;
|
|
|
|
if($mybb->usergroup['maxwarningsday'] != 0)
|
|
{
|
|
$timecut = TIME_NOW-60*60*24;
|
|
$query = $db->simple_select("warnings", "COUNT(wid) AS given_today", "issuedby='{$mybb->user['uid']}' AND dateline>'$timecut'");
|
|
$given_today = $db->fetch_field($query, "given_today");
|
|
if($given_today >= $mybb->usergroup['maxwarningsday'])
|
|
{
|
|
$this->set_error('reached_max_warnings_day', array(my_number_format($mybb->usergroup['maxwarningsday'])));
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Validate warnings type.
|
|
*
|
|
* @return boolean True when valid, false when invalid.
|
|
*/
|
|
function validate_type()
|
|
{
|
|
global $mybb, $db;
|
|
|
|
$warning = &$this->data;
|
|
|
|
// Issuing a custom warning
|
|
if($warning['type'] == 'custom')
|
|
{
|
|
if($mybb->settings['allowcustomwarnings'] == 0)
|
|
{
|
|
$this->set_error('error_cant_custom_warn');
|
|
return false;
|
|
}
|
|
|
|
if(!$warning['custom_reason'])
|
|
{
|
|
$this->set_error('error_no_custom_reason');
|
|
return false;
|
|
}
|
|
|
|
$warning['title'] = $warning['custom_reason'];
|
|
|
|
if(!$warning['custom_points'] || $warning['custom_points'] > $mybb->settings['maxwarningpoints'] || $warning['custom_points'] < 0)
|
|
{
|
|
$this->set_error('error_invalid_custom_points', array(my_number_format($mybb->settings['maxwarningpoints'])));
|
|
return false;
|
|
}
|
|
|
|
$warning['points'] = round($warning['custom_points']);
|
|
|
|
// Build expiry date
|
|
if($warning['expires_period'] == "hours")
|
|
{
|
|
$warning['expires'] = $warning['expires']*3600 + TIME_NOW;
|
|
}
|
|
else if($warning['expires_period'] == "days")
|
|
{
|
|
$warning['expires'] = $warning['expires']*86400 + TIME_NOW;
|
|
}
|
|
else if($warning['expires_period'] == "weeks")
|
|
{
|
|
$warning['expires'] = $warning['expires']*604800 + TIME_NOW;
|
|
}
|
|
else if($warning['expires_period'] == "months")
|
|
{
|
|
$warning['expires'] = $warning['expires']*2592000 + TIME_NOW;
|
|
}
|
|
else if($warning['expires_period'] == "never")
|
|
{
|
|
$warning['expires'] = 0;
|
|
}
|
|
else
|
|
{
|
|
// unkown expires_period
|
|
$this->set_error('error_invalid_expires_period');
|
|
return false;
|
|
}
|
|
}
|
|
// Using a predefined warning type
|
|
else
|
|
{
|
|
$query = $db->simple_select("warningtypes", "*", "tid='".(int)$warning['type']."'");
|
|
$this->warning_type = $db->fetch_array($query);
|
|
|
|
if(!$this->warning_type)
|
|
{
|
|
$this->set_error('error_invalid_type');
|
|
return false;
|
|
}
|
|
|
|
$warning['points'] = $this->warning_type['points'];
|
|
$warning['title'] = '';
|
|
$warning['expires'] = 0;
|
|
|
|
if($this->warning_type['expirationtime'])
|
|
{
|
|
$warning['expires'] = TIME_NOW+$this->warning_type['expirationtime'];
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Validate a warning.
|
|
*
|
|
* @return boolean True when valid, false when invalid.
|
|
*/
|
|
function validate_warning()
|
|
{
|
|
global $plugins;
|
|
|
|
$warning = &$this->data;
|
|
|
|
// Verify all warning assets.
|
|
$this->validate_user();
|
|
$this->validate_maximum();
|
|
$this->validate_notes();
|
|
|
|
if(array_key_exists('pid', $warning))
|
|
{
|
|
$this->validate_post();
|
|
}
|
|
if(array_key_exists('type', $warning))
|
|
{
|
|
$this->validate_type();
|
|
}
|
|
|
|
$plugins->run_hooks("datahandler_warnings_validate_warning", $this);
|
|
|
|
// We are done validating, return.
|
|
$this->set_validated(true);
|
|
|
|
if(count($this->get_errors()) > 0)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Gets a valid warning from the DB engine.
|
|
*
|
|
* @param int $wid
|
|
* @return array|bool array when valid, boolean false when invalid.
|
|
*/
|
|
function get($wid)
|
|
{
|
|
global $db;
|
|
|
|
$wid = (int)$wid;
|
|
if($wid <= 0)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
$query = $db->simple_select("warnings", "*", "wid='".$wid."'");
|
|
$this->read_warning_data = $db->fetch_array($query);
|
|
|
|
if(!$this->read_warning_data['wid'])
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return $this->read_warning_data;
|
|
}
|
|
|
|
/**
|
|
* Expire old warnings in the database.
|
|
*
|
|
* @return boolean True when finished.
|
|
*/
|
|
function expire_warnings()
|
|
{
|
|
global $db;
|
|
|
|
$users = array();
|
|
|
|
$query = $db->query("
|
|
SELECT w.wid, w.uid, w.points, u.warningpoints
|
|
FROM ".TABLE_PREFIX."warnings w
|
|
LEFT JOIN ".TABLE_PREFIX."users u ON (u.uid=w.uid)
|
|
WHERE expires<".TIME_NOW." AND expires!=0 AND expired!=1
|
|
");
|
|
while($warning = $db->fetch_array($query))
|
|
{
|
|
$updated_warning = array(
|
|
"expired" => 1
|
|
);
|
|
$db->update_query("warnings", $updated_warning, "wid='{$warning['wid']}'");
|
|
|
|
if(array_key_exists($warning['uid'], $users))
|
|
{
|
|
$users[$warning['uid']] -= $warning['points'];
|
|
}
|
|
else
|
|
{
|
|
$users[$warning['uid']] = $warning['warningpoints']-$warning['points'];
|
|
}
|
|
}
|
|
|
|
foreach($users as $uid => $warningpoints)
|
|
{
|
|
if($warningpoints < 0)
|
|
{
|
|
$warningpoints = 0;
|
|
}
|
|
|
|
$updated_user = array(
|
|
"warningpoints" => (int)$warningpoints
|
|
);
|
|
$db->update_query("users", $updated_user, "uid='".(int)$uid."'");
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Updates an user warning details.
|
|
*
|
|
* @return array Updated user details.
|
|
*/
|
|
function update_user($method='insert')
|
|
{
|
|
global $db, $mybb, $lang, $cache, $groupscache;
|
|
|
|
if($mybb->settings['maxwarningpoints'] < 1)
|
|
{
|
|
$mybb->settings['maxwarningpoints'] = 10;
|
|
}
|
|
|
|
if(!is_array($groupscache))
|
|
{
|
|
$groupscache = $cache->read("usergroups");
|
|
}
|
|
|
|
$warning = &$this->data;
|
|
|
|
$user = get_user($warning['uid']);
|
|
|
|
if($method == 'insert')
|
|
{
|
|
// Build warning level & ensure it doesn't go over 100.
|
|
$current_level = round($user['warningpoints']/$mybb->settings['maxwarningpoints']*100);
|
|
$this->new_warning_level = round(($user['warningpoints']+$warning['points'])/$mybb->settings['maxwarningpoints']*100);
|
|
if($this->new_warning_level > 100)
|
|
{
|
|
$this->new_warning_level = 100;
|
|
}
|
|
|
|
// Update user
|
|
$this->updated_user = array(
|
|
"warningpoints" => $user['warningpoints']+$warning['points']
|
|
);
|
|
|
|
// Fetch warning level
|
|
$query = $db->simple_select("warninglevels", "*", "percentage<={$this->new_warning_level}", array("order_by" => "percentage", "order_dir" => "desc"));
|
|
$new_level = $db->fetch_array($query);
|
|
|
|
if($new_level['lid'])
|
|
{
|
|
$expiration = 0;
|
|
$action = my_unserialize($new_level['action']);
|
|
|
|
if($action['length'] > 0)
|
|
{
|
|
$expiration = TIME_NOW+$action['length'];
|
|
}
|
|
|
|
switch($action['type'])
|
|
{
|
|
// Ban the user for a specified time
|
|
case 1:
|
|
// Fetch any previous bans for this user
|
|
$query = $db->simple_select("banned", "*", "uid='{$user['uid']}' AND gid='{$action['usergroup']}' AND lifted>".TIME_NOW);
|
|
$existing_ban = $db->fetch_array($query);
|
|
|
|
// Only perform if no previous ban or new ban expires later than existing ban
|
|
if(($expiration > $existing_ban['lifted'] && $existing_ban['lifted'] != 0) || $expiration == 0 || !$existing_ban['uid'])
|
|
{
|
|
if(!$warning['title'])
|
|
{
|
|
$warning['title'] = $this->warning_type['title'];
|
|
}
|
|
|
|
// Never lift the ban?
|
|
if($action['length'] <= 0)
|
|
{
|
|
$bantime = '---';
|
|
}
|
|
else
|
|
{
|
|
$bantimes = fetch_ban_times();
|
|
foreach($bantimes as $date => $string)
|
|
{
|
|
if($date == '---')
|
|
{
|
|
continue;
|
|
}
|
|
|
|
$time = 0;
|
|
list($day, $month, $year) = explode('-', $date);
|
|
if($day > 0)
|
|
{
|
|
$time += 60*60*24*$day;
|
|
}
|
|
|
|
if($month > 0)
|
|
{
|
|
$time += 60*60*24*30*$month;
|
|
}
|
|
|
|
if($year > 0)
|
|
{
|
|
$time += 60*60*24*365*$year;
|
|
}
|
|
|
|
if($time == $action['length'])
|
|
{
|
|
$bantime = $date;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
$new_ban = array(
|
|
"uid" => $user['uid'],
|
|
"gid" => $action['usergroup'],
|
|
"oldgroup" => $user['usergroup'],
|
|
"oldadditionalgroups" => $user['additionalgroups'],
|
|
"olddisplaygroup" => $user['displaygroup'],
|
|
"admin" => $mybb->user['uid'],
|
|
"dateline" => TIME_NOW,
|
|
"bantime" => $db->escape_string($bantime),
|
|
"lifted" => $expiration,
|
|
"reason" => $db->escape_string($warning['title'])
|
|
);
|
|
// Delete old ban for this user, taking details
|
|
if($existing_ban['uid'])
|
|
{
|
|
$db->delete_query("banned", "uid='{$user['uid']}' AND gid='{$action['usergroup']}'");
|
|
// Override new ban details with old group info
|
|
$new_ban['oldgroup'] = $existing_ban['oldgroup'];
|
|
$new_ban['oldadditionalgroups'] = $existing_ban['oldadditionalgroups'];
|
|
$new_ban['olddisplaygroup'] = $existing_ban['olddisplaygroup'];
|
|
}
|
|
|
|
$period = $lang->expiration_never;
|
|
$ban_length = fetch_friendly_expiration($action['length']);
|
|
|
|
if($ban_length['time'])
|
|
{
|
|
$lang_str = "expiration_".$ban_length['period'];
|
|
$period = $lang->sprintf($lang->result_period, $ban_length['time'], $lang->$lang_str);
|
|
}
|
|
|
|
$group_name = $groupscache[$action['usergroup']]['title'];
|
|
$this->friendly_action = $lang->sprintf($lang->redirect_warned_banned, $group_name, $period);
|
|
|
|
$db->insert_query("banned", $new_ban);
|
|
$this->updated_user['usergroup'] = $action['usergroup'];
|
|
$this->updated_user['additionalgroups'] = '';
|
|
$this->updated_user['displaygroup'] = 0;
|
|
}
|
|
break;
|
|
// Suspend posting privileges
|
|
case 2:
|
|
// Only perform if the expiration time is greater than the users current suspension period
|
|
if($expiration == 0 || $expiration > $user['suspensiontime'])
|
|
{
|
|
if(($user['suspensiontime'] != 0 && $user['suspendposting']) || !$user['suspendposting'])
|
|
{
|
|
$period = $lang->expiration_never;
|
|
$ban_length = fetch_friendly_expiration($action['length']);
|
|
|
|
if($ban_length['time'])
|
|
{
|
|
$lang_str = "expiration_".$ban_length['period'];
|
|
$period = $lang->sprintf($lang->result_period, $ban_length['time'], $lang->$lang_str);
|
|
}
|
|
|
|
$this->friendly_action = $lang->sprintf($lang->redirect_warned_suspended, $period);
|
|
|
|
$this->updated_user['suspensiontime'] = $expiration;
|
|
$this->updated_user['suspendposting'] = 1;
|
|
}
|
|
}
|
|
break;
|
|
// Moderate new posts
|
|
case 3:
|
|
// Only perform if the expiration time is greater than the users current suspension period
|
|
if($expiration == 0 || $expiration > $user['moderationtime'])
|
|
{
|
|
if(($user['moderationtime'] != 0 && $user['moderateposts']) || !$user['suspendposting'])
|
|
{
|
|
$period = $lang->expiration_never;
|
|
$ban_length = fetch_friendly_expiration($action['length']);
|
|
|
|
if($ban_length['time'])
|
|
{
|
|
$lang_str = "expiration_".$ban_length['period'];
|
|
$period = $lang->sprintf($lang->result_period, $ban_length['time'], $lang->$lang_str);
|
|
}
|
|
|
|
$this->friendly_action = $lang->sprintf($lang->redirect_warned_moderate, $period);
|
|
|
|
$this->updated_user['moderationtime'] = $expiration;
|
|
$this->updated_user['moderateposts'] = 1;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Warning is still active, lower users point count
|
|
if($warning['expired'] != 1)
|
|
{
|
|
$new_warning_points = $user['warningpoints']-$warning['points'];
|
|
if($new_warning_points < 0)
|
|
{
|
|
$new_warning_points = 0;
|
|
}
|
|
|
|
$this->updated_user = array(
|
|
"warningpoints" => $new_warning_points
|
|
);
|
|
|
|
|
|
// check if we need to revoke any consequences with this warning
|
|
$current_level = round($user['warningpoints']/$mybb->settings['maxwarningpoints']*100);
|
|
$this->new_warning_level = round($new_warning_points/$mybb->settings['maxwarningpoints']*100);
|
|
$query = $db->simple_select("warninglevels", "action", "percentage>{$this->new_warning_level} AND percentage<=$current_level");
|
|
if($db->num_rows($query))
|
|
{
|
|
// we have some warning levels we need to revoke
|
|
$max_expiration_times = $check_levels = array();
|
|
find_warnlevels_to_check($query, $max_expiration_times, $check_levels);
|
|
|
|
// now check warning levels already applied to this user to see if we need to lower any expiration times
|
|
$query = $db->simple_select("warninglevels", "action", "percentage<={$this->new_warning_level}");
|
|
$lower_expiration_times = $lower_levels = array();
|
|
find_warnlevels_to_check($query, $lower_expiration_times, $lower_levels);
|
|
|
|
// now that we've got all the info, do necessary stuff
|
|
for($i = 1; $i <= 3; ++$i)
|
|
{
|
|
if($check_levels[$i])
|
|
{
|
|
switch($i)
|
|
{
|
|
case 1: // Ban
|
|
// we'll have to resort to letting the admin/mod remove the ban manually, since there's an issue if stacked bans are in force...
|
|
continue 2;
|
|
case 2: // Revoke posting
|
|
$current_expiry_field = 'suspensiontime';
|
|
$current_inforce_field = 'suspendposting';
|
|
break;
|
|
case 3:
|
|
$current_expiry_field = 'moderationtime';
|
|
$current_inforce_field = 'moderateposts';
|
|
break;
|
|
}
|
|
|
|
// if the thing isn't in force, don't bother with trying to update anything
|
|
if(!$user[$current_inforce_field])
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if($lower_levels[$i])
|
|
{
|
|
// lessen the expiration time if necessary
|
|
|
|
if(!$lower_expiration_times[$i])
|
|
{
|
|
// doesn't expire - enforce this
|
|
$this->updated_user[$current_expiry_field] = 0;
|
|
continue;
|
|
}
|
|
|
|
if($max_expiration_times[$i])
|
|
{
|
|
// if the old level did have an expiry time...
|
|
if($max_expiration_times[$i] <= $lower_expiration_times[$i])
|
|
{
|
|
// if the lower expiration time is actually higher than the upper expiration time -> skip
|
|
continue;
|
|
}
|
|
// both new and old max expiry times aren't infinite, so we can take a difference
|
|
$expire_offset = ($lower_expiration_times[$i] - $max_expiration_times[$i]);
|
|
}
|
|
else
|
|
{
|
|
// the old level never expired, not much we can do but try to estimate a new expiry time... which will just happen to be starting from today...
|
|
$expire_offset = TIME_NOW + $lower_expiration_times[$i];
|
|
// if the user's expiry time is already less than what we're going to set it to, skip
|
|
if($user[$current_expiry_field] <= $expire_offset)
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
|
|
$this->updated_user[$current_expiry_field] = $user[$current_expiry_field] + $expire_offset;
|
|
// double-check if it's expired already
|
|
if($this->updated_user[$current_expiry_field] < TIME_NOW)
|
|
{
|
|
$this->updated_user[$current_expiry_field] = 0;
|
|
$this->updated_user[$current_inforce_field] = 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// there's no lower level for this type - remove the consequence entirely
|
|
$this->updated_user[$current_expiry_field] = 0;
|
|
$this->updated_user[$current_inforce_field] = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Save updated details
|
|
$db->update_query("users", $this->updated_user, "uid='{$user['uid']}'");
|
|
|
|
$mybb->cache->update_moderators();
|
|
|
|
return $this->updated_user;
|
|
}
|
|
|
|
/**
|
|
* Inserts a warning into the database
|
|
*
|
|
* @return array Warning database details.
|
|
*/
|
|
function insert_warning()
|
|
{
|
|
global $db, $mybb, $plugins;
|
|
|
|
$warning = &$this->data;
|
|
|
|
$this->write_warning_data = array(
|
|
"uid" => (int)$warning['uid'],
|
|
"tid" => (int)$warning['type'],
|
|
"pid" => (int)$warning['pid'],
|
|
"title" => $db->escape_string($warning['title']),
|
|
"points" => (int)$warning['points'],
|
|
"dateline" => TIME_NOW,
|
|
"issuedby" => $mybb->user['uid'],
|
|
"expires" => (int)$warning['expires'],
|
|
"expired" => 0,
|
|
"revokereason" => '',
|
|
"notes" => $db->escape_string($warning['notes'])
|
|
);
|
|
|
|
$this->write_warning_data['wid'] = $db->insert_query("warnings", $this->write_warning_data);
|
|
|
|
$this->update_user();
|
|
|
|
$plugins->run_hooks("datahandler_warnings_insert_warning", $this);
|
|
|
|
return $this->write_warning_data;
|
|
}
|
|
|
|
/**
|
|
* Updates a warning in the database
|
|
*
|
|
* @return array Warning database details.
|
|
*/
|
|
function update_warning()
|
|
{
|
|
global $db, $mybb, $plugins;
|
|
|
|
$warning = &$this->data;
|
|
|
|
$warning['wid'] = (int)$warning['wid'];
|
|
if($warning['wid'] <= 0)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
$this->write_warning_data = array(
|
|
"expired" => 1,
|
|
"daterevoked" => TIME_NOW,
|
|
"revokedby" => $mybb->user['uid'],
|
|
"revokereason" => $db->escape_string($warning['reason'])
|
|
);
|
|
|
|
$plugins->run_hooks("datahandler_warnings_update_warning", $this);
|
|
|
|
$db->update_query("warnings", $this->write_warning_data, "wid='{$warning['wid']}'");
|
|
|
|
$this->update_user('update');
|
|
|
|
return $this->write_warning_data;
|
|
}
|
|
|
|
}
|
|
|