<?php
/***************************************************************************
* copyright            : (C) 2001-2003 Advanced Internet Designs Inc.
* email                : forum@prohost.org
* $Id: consist.php,v 1.1.1.1.8.1 2004/11/24 15:23:23 kazuyan Exp $
*
* This program is free software; you can redistribute it and/or modify it 
* under the terms of the GNU General Public License as published by the 
* Free Software Foundation; either version 2 of the License, or 
* (at your option) any later version.
***************************************************************************/

	@set_time_limit(600);
	@ini_set("memory_limit", "100M");
	define('back_to_main', 1);

	require('./GLOBALS.php'); fud_phpgw();

	fud_use('adm.inc', true);
	fud_use('glob.inc', true);
	fud_use('widgets.inc', true);
	fud_use('ext.inc', true);
	fud_use('ipfilter.inc', true);
	fud_use('login_filter.inc', true);
	fud_use('email_filter.inc', true);
	fud_use('customtags.inc', true);
	fud_use('groups_adm.inc', true);
	fud_use('cat.inc', true);
	fud_use('imsg.inc');
	fud_use('imsg_edt.inc');
	fud_use('err.inc');
	fud_use('private.inc');
	fud_use('th.inc');
	fud_use('ipoll.inc');
	fud_use('attach.inc');
	fud_use('groups.inc');
	fud_use('th_adm.inc');
	fud_use('users_reg.inc');

function draw_stat($text)
{
	echo '<b>'.htmlspecialchars($text).'</b><br />' . "\n";
	flush();
}

function draw_info($cnt)
{
	draw_stat(($cnt < 1 ? 'OK' : lang('%1 entries unmatched, deleted', $cnt)));
}

function delete_zero($tbl, $q)
{
	$cnt = 0;
	$c = q($q);
	while ($r = db_rowarr($c)) {
		$a[] = $r[0];
		++$cnt;
	}
	if ($cnt) {
		q('DELETE FROM '.$tbl.' WHERE id IN ('.implode(',', $a).')');
	}
	unset($c);
	draw_info($cnt);
}

	include($WWW_ROOT_DISK . 'adm/admpanel.php');

	if (!isset($_POST['conf']) && !isset($_GET['enable_forum']) && !isset($_GET['opt'])) {
?>
<form method="post" action="consist.php">
<div align="center">
<?php echo lang('Consistency check is a complex process which may take several minutes to run, while it is running your forum will be disabled.') ?>
<br><br>
<h2><?php echo lang('Do you wish to proceed?') ?></h2>
<input type="submit" name="cancel" value="<?php echo lang('No') ?>">&nbsp;&nbsp;&nbsp;<input type="submit" name="conf" value="<?php echo lang('Yes') ?>">
</div>
<?php echo _hs; ?>
</form>
<?php
		require($WWW_ROOT_DISK . 'adm/admclose.php');
		exit;
	}

	if ($FUD_OPT_1 & 1) {
		draw_stat(lang('Disabling the forum for the duration of maintenance run'));
		maintenance_status(lang('Undergoing maintenance, please come back later.'), 1);
	}
	if (isset($_GET['opt'])) {
		draw_stat(lang('Optimizing forum\'s SQL tables'));
		optimize_tables();
		draw_stat(lang('Done').': '.lang('Optimizing forum\'s SQL tables'));

		if ($FUD_OPT_1 & 1 || isset($_GET['enable_forum'])) {
			draw_stat(lang('Re-enabling the forum.'));
			maintenance_status($DISABLED_REASON, 0);
		} else {
			echo '<font size="+1" color="red">'.lang('Your forum is currently disabled, to re-enable it go to the <a href="admglobal.php?%1">Global Settings Manager</a> and re-enable it.', '._rsid.').'</font><br>';
		}

		require($WWW_ROOT_DISK . 'adm/admclose.php');
		exit;
	}
?>
<script language="Javascript1.2">
	var intervalID;
	function scrolldown()
	{
		window.scroll(0, 30000);
	}
	intervalID = setInterval('scrolldown()', 100);
</script>
<?php
	$tbl = $DBHOST_TBL_PREFIX;

	draw_stat(lang('Locking the database for checking'));
	if (__dbtype__ == 'mysql') {
		q('DROP TABLE IF EXISTS '.$tbl.'tmp_consist');
		q('CREATE TABLE '.$tbl.'tmp_consist (p INT, ps INT UNSIGNED, c INT)');
	}
	$tbls = get_fud_table_list();
	// add the various table aliases
	array_push($tbls, 	$tbl.'users u', $tbl.'forum f', $tbl.'thread t', $tbl.'poll p', $tbl.'poll_opt po', $tbl.'poll_opt_track pot',
				$tbl.'msg m', $tbl.'pmsg pm', $tbl.'mod mm', $tbl.'thread_rate_track trt', $tbl.'msg_report mr', $tbl.'cat c',
				$tbl.'forum_notify fn', $tbl.'thread_notify tn', $tbl.'buddy b', $tbl.'user_ignore i', $tbl.'msg m1', $tbl.'msg m2',
				$tbl.'users u1', $tbl.'users u2', $tbl.'attach a', $tbl.'thr_exchange te', $tbl.'read r', $tbl.'mime mi',
				$tbl.'group_members gm', $tbl.'group_resources gr', $tbl.'groups g', $tbl.'group_members gm1', $tbl.'group_members gm2', $tbl.'themes thm');

	db_lock(implode(' WRITE, ', $tbls).' WRITE');
	draw_stat(lang('Locked!'));

	draw_stat(lang('Validating category order'));
	$i = 1;
	$c = q('SELECT id, view_order FROM '.$tbl.'cat ORDER BY view_order, id');
	while ($r = db_rowarr($c)) {
		if ($r[1] != $i) {
			q('UPDATE '.$tbl.'cat SET view_order='.$i.' WHERE id='.$id);
		}
		++$i;
	}
	unset($r);
	draw_stat(lang('Done').': '.lang('Validating category order'));

	draw_stat(lang('Checking if moderator and users table match'));
	delete_zero($tbl.'mod', 'SELECT mm.id FROM '.$tbl.'mod mm LEFT JOIN '.$tbl.'users u ON mm.user_id=u.id LEFT JOIN '.$tbl.'forum f ON f.id=mm.forum_id WHERE u.id IS NULL OR f.id IS NULL');

	draw_stat(lang('Rebuilding moderators'));
	rebuildmodlist();
	draw_stat(lang('Done').': '.lang('Rebuilding moderators'));

	draw_stat(lang('Checking if all private messages have users'));
	$c = uq('SELECT pm.id FROM '.$tbl.'pmsg pm LEFT JOIN '.$tbl.'users u ON u.id=pm.ouser_id WHERE (pm.pmsg_opt & 16)=0 AND u.id IS NULL');
	while ($r = db_rowarr($c)) {
		$dpm[] = $r[0];
	}
	$c = uq('SELECT pm.id FROM '.$tbl.'pmsg pm LEFT JOIN '.$tbl.'users u ON u.id=pm.duser_id WHERE ((pm.pmsg_opt & 16) > 0 AND pm.pmsg_opt>=16) AND u.id IS NULL');
	while ($r = db_rowarr($c)) {
		$dpm[] = $r[0];
	}
	if (isset($dpm)) {
		$cnt = count($dpm);
		foreach ($dpm as $v) {
			pmsg_del($v, 5);
		}
	} else {
		$cnt = 0;
	}
	draw_info($cnt);

	draw_stat(lang('Checking messages against users & threads'));
	delete_zero($tbl.'msg', 'SELECT m.id FROM '.$tbl.'msg m LEFT JOIN '.$tbl.'users u ON u.id=m.poster_id LEFT JOIN '.$tbl.'thread t ON t.id=m.thread_id LEFT JOIN '.$tbl.'forum f ON f.id=t.forum_id WHERE (m.poster_id!=0 AND u.id IS NULL) OR t.id IS NULL OR f.id IS NULL');

	draw_stat(lang('Checking threads against forums'));
	delete_zero($tbl.'thread', 'SELECT t.id FROM '.$tbl.'thread t LEFT JOIN '.$tbl.'forum f ON f.id=t.forum_id WHERE f.id IS NULL');

	draw_stat(lang('Checking message approvals'));
	$m = array();
	$c = uq('SELECT m.id FROM '.$tbl.'msg m INNER JOIN '.$tbl.'thread t ON m.thread_id=t.id INNER JOIN '.$tbl.'forum f ON t.forum_id=f.id WHERE m.apr=0 AND (f.forum_opt & 2) > 0');
	while ($r = db_rowarr($c)) {
		$m[] = $r[0];
	}
	if (count($m)) {
		q('UPDATE '.$tbl.'msg SET apr=1 WHERE id IN('.implode(',', $m).')');
		unset($m);
	}
	draw_stat(lang('Done').': '.lang('Checking message approvals'));

	$cnt = 0;
	$del = $tr = array();
	draw_stat(lang('Checking threads against messages'));
	q('UPDATE '.$tbl.'thread SET replies=0');
	$c = uq('SELECT m.thread_id, t.id, count(*) as cnt FROM '.$tbl.'thread t LEFT JOIN '.$tbl.'msg m ON t.id=m.thread_id WHERE m.apr=1 GROUP BY m.thread_id,t.id ORDER BY cnt');
	while ($r = db_rowarr($c)) {
		if (!$r[0]) {
			$del[] = $r[1];
			++$cnt;
		} else {
			$tr[$r[2] - 1][] = $r[1];
		}
	}
	if (count($del)) {
		q('DELETE FROM '.$tbl.'thread WHERE id='.implode(',', $del));
	}
	unset($tr[0]);
	foreach ($tr as $k => $v) {
		q('UPDATE '.$tbl.'thread SET replies='.$k.' WHERE id IN('.implode(',', $v).')');
	}
	unset($tr, $del);
	draw_info($cnt);

	draw_stat(lang('Checking thread last & first post ids'));
	$c = q('SELECT m1.id, m2.id, t.id FROM '.$tbl.'thread t LEFT JOIN '.$tbl.'msg m1 ON t.root_msg_id=m1.id LEFT JOIN '.$tbl.'msg m2 ON t.last_post_id=m2.id WHERE m1.id IS NULL or m2.id IS NULL');
	while ($r = db_rowarr($c)) {
		if (!$r[0]) {
			if (!($root = q_singleval('SELECT id FROM '.$tbl.'msg WHERE thread_id='.$r[2].' ORDER BY post_stamp LIMIT 1'))) {
				q('DELETE FROM '.$tbl.'thread WHERE id='.$r[2]);
			} else {
				q('UPDATE '.$tbl.'thread SET root_msg_id='.$root.' WHERE id='.$r[2]);
			}
		} else {
			$r2 = db_saq('SELECT id, post_stamp FROM '.$tbl.'msg WHERE thread_id='.$r[2].' ORDER BY post_stamp DESC LIMIT 1');
			if (!$r2) {
				q('DELETE FROM '.$tbl.'thread WHERE id='.$r[2]);
			} else {
				q('UPDATE '.$tbl.'thread SET last_post_id='.$r2[0].', last_post_date='.$r2[1].' WHERE id='.$r[2]);
			}
		}
	}
	draw_stat(lang('Done').': '.lang('Checking thread last & first post ids'));

	draw_stat(lang('Checking forum & topic relations'));
	$c = q('SELECT id FROM '.$tbl.'forum');
	while ($f = db_rowarr($c)) {
		$r = db_saq('select MAX(last_post_id), SUM(replies), COUNT(*) FROM '.$tbl.'thread t INNER JOIN '.$tbl.'msg m ON t.root_msg_id=m.id AND m.apr=1 WHERE t.forum_id='.$f[0]);
		if (!$r[2]) {
			q('UPDATE '.$tbl.'forum SET thread_count=0, post_count=0, last_post_id=0 WHERE id='.$f[0]);
		} else {
			q('UPDATE '.$tbl.'forum SET thread_count='.$r[2].', post_count='.($r[1] + $r[2]).', last_post_id='.(int)$r[0].' WHERE id='.$f[0]);
		}
	}
	unset($c);
	draw_stat(lang('Done').': '.lang('Checking forum & topic relations'));

	draw_stat(lang('Validating Forum Order'));
	$cat = 0;
	$c = q('SELECT id, cat_id, view_order FROM '.$tbl.'forum WHERE cat_id>0 ORDER BY cat_id, view_order');
	while ($f = db_rowarr($c)) {
		if ($cat != $f[1]) {
			$i = 0;
			$cat = $f[1];
		}
		++$i;
		if ($i != $f[2]) {
			q('UPDATE '.$tbl.'forum SET view_order='.$i.' WHERE id='.$f[0]);
		}
	}
	draw_stat(lang('Done').': '.lang('Validating Forum Order'));

	draw_stat(lang('Checking thread_exchange'));
	delete_zero($tbl.'thr_exchange', 'SELECT te.id FROM '.$tbl.'thr_exchange te LEFT JOIN '.$tbl.'thread t ON t.id=te.th LEFT JOIN '.$tbl.'forum f ON f.id=te.frm WHERE t.id IS NULL or f.id IS NULL');

	draw_stat(lang('Checking read table against users & threads'));
	delete_zero($tbl.'read', 'SELECT r.id FROM '.$tbl.'read r LEFT JOIN '.$tbl.'users u ON r.user_id=u.id LEFT JOIN '.$tbl.'thread t ON r.thread_id=t.id WHERE t.id IS NULL OR u.id IS NULL');

	draw_stat(lang('Checking file attachments against messages'));
	$arm = array();
	$c = uq('SELECT a.id FROM '.$tbl.'attach a LEFT JOIN '.$tbl.'msg m ON a.message_id=m.id WHERE m.id IS NULL AND attach_opt=0');
	while ($r = db_rowarr($c)) {
		$arm[] = $r[0];
	}
	$c = uq('SELECT a.id FROM '.$tbl.'attach a LEFT JOIN '.$tbl.'pmsg pm ON a.message_id=pm.id WHERE pm.id IS NULL AND attach_opt=1');
	while ($r = db_rowarr($c)) {
		$arm[] = $r[0];
	}
	if (($cnt = count($arm))) {
		foreach ($arm as $a) {
			@unlink($FILE_STORE . $a . 'atch');
		}
		q('DELETE FROM '.$tbl.'attach WHERE id IN('.implode(',', $arm).')');
	}
	draw_info($cnt);

	draw_stat(lang('Rebuild attachment cache for regular messages'));
	$oldm = '';
	$atr = array();
	q('UPDATE '.$tbl.'msg SET attach_cnt=0, attach_cache=NULL');
	$c = q('SELECT a.id, a.original_name, a.fsize, a.dlcount, CASE WHEN mi.icon IS NULL THEN \'unknown.gif\' ELSE mi.icon END, a.message_id FROM '.$tbl.'attach a LEFT JOIN '.$tbl.'mime mi ON a.mime_type=mi.id WHERE attach_opt=0');
	while ($r = db_rowarr($c)) {
		if ($oldm != $r[5]) {
			if ($oldm) {
				q('UPDATE '.$tbl.'msg SET attach_cnt='.count($atr).', attach_cache='.strnull(addslashes(@serialize($atr))).' WHERE id='.$oldm);
				$atr = array();
			}
			$oldm = $r[5];
		}
		unset($r[5]);
		$atr[] = $r;
	}
	unset($c);
	if (count($atr)) {
		q('UPDATE '.$tbl.'msg SET attach_cnt='.count($atr).', attach_cache='.strnull(addslashes(@serialize($atr))).' WHERE id='.$oldm);
	}
	draw_stat(lang('Done').': '.lang('Rebuild attachment cache for regular messages'));

	draw_stat(lang('Rebuild attachment cache for private messages'));
	q('UPDATE '.$tbl.'pmsg SET attach_cnt=0');
	$c = q('SELECT count(*), message_id FROM '.$tbl.'attach WHERE attach_opt=1 GROUP BY message_id');
	while ($r = db_rowarr($c)) {
		q('UPDATE '.$tbl.'pmsg SET attach_cnt='.$r[0].' WHERE id='.$r[1]);
	}
	unset($c);
	draw_stat(lang('Done').': '.lang('Rebuild attachment cache for private messages'));

	draw_stat(lang('Checking message reports'));
	delete_zero($tbl.'msg_report', 'SELECT mr.id FROM '.$tbl.'msg_report mr LEFT JOIN '.$tbl.'msg m ON mr.msg_id=m.id WHERE m.id IS NULL');

	draw_stat(lang('Checking polls against messages'));
	$cnt = 0;
	$c = q('SELECT p.id, m.id FROM '.$tbl.'poll p LEFT JOIN '.$tbl.'msg m ON p.id=m.poll_id WHERE m.id IS NULL OR p.id IS NULL');
	while ($r = db_rowarr($c)) {
		if ($r[0]) {
			q('DELETE FROM '.$tbl.'poll WHERE id='.$r[0]);
			++$cnt;
		} else {
			q('UPDATE '.$tbl.'msg SET poll_id=0, poll_cache=NULL WHERE id='.$r[1]);
		}
	}
	unset($c);
	draw_info($cnt);

	draw_stat(lang('Checking polls options against polls'));
	delete_zero($tbl.'poll_opt', 'SELECT po.id FROM '.$tbl.'poll_opt po LEFT JOIN '.$tbl.'poll p ON p.id=po.poll_id WHERE p.id IS NULL');

	draw_stat(lang('Checking polls votes'));
	delete_zero($tbl.'poll_opt_track', 'SELECT pot.id FROM '.$tbl.'poll_opt_track pot LEFT JOIN '.$tbl.'poll p ON p.id=pot.poll_id LEFT JOIN '.$tbl.'poll_opt po ON po.id=pot.poll_opt LEFT JOIN '.$tbl.'users u ON u.id=pot.user_id WHERE u.id IS NULL OR po.id IS NULL OR p.id IS NULL');

	draw_stat(lang('Rebuilding poll cache'));
	// first we validate to vote counts for each option
	q('UPDATE '.$tbl.'poll_opt SET count=0');
	$c = q('SELECT poll_opt, count(*) FROM '.$tbl.'poll_opt_track GROUP BY poll_opt');
	while ($r = db_rowarr($c)) {
		q('UPDATE '.$tbl.'poll_opt SET count='.(int)$r[1].' WHERE id='.$r[0]);
	}
	unset($c);

	// now we rebuild the individual message poll cache
	$oldp = '';
	$opts = array();
	$vt = 0;
	$c = q('SELECT id, name, count, poll_id FROM '.$tbl.'poll_opt ORDER BY poll_id');
	while ($r = db_rowarr($c)) {
		if ($oldp != $r[3]) {
			if ($oldp) {
				q('UPDATE '.$tbl.'msg SET poll_cache='.strnull(addslashes(@serialize($opts))).' WHERE poll_id='.$oldp);
				q('UPDATE '.$tbl.'poll SET total_votes='.$vt.' WHERE id='.$oldp);
				$opts = array();
				$vt = 0;
			}
			$oldp = $r[3];
		}
		$opts[$r[0]] = array($r[1], $r[2]);
		$vt += $r[2];
	}
	unset($c);
	if (count($opts)) {
		q('UPDATE '.$tbl.'msg SET poll_cache='.strnull(addslashes(@serialize($opts))).' WHERE poll_id='.$oldp);
		q('UPDATE '.$tbl.'poll SET total_votes='.$vt.' WHERE id='.$oldp);
	}
	draw_stat(lang('Done').': '.lang('Rebuilding poll cache'));

	draw_stat(lang('Validating poll activation'));
	$c = q('SELECT t.forum_id, p.id FROM '.$tbl.'poll p INNER JOIN '.$tbl.'msg m ON m.poll_id=p.id INNER JOIN '.$tbl.'thread t ON m.thread_id=t.id AND m.apr=1 WHERE t.forum_id!=p.forum_id');
	while ($r = db_rowarr($c)) {
		q('UPDATE '.$tbl.'poll SET forum_id='.$r[0].' WHERE id='.$r[1]);
	}
	unset($c);
	draw_stat(lang('Done').': '.lang('Validating poll activation'));

	draw_stat(lang('Checking smilies against disk files'));
	$cnt = $i = 0;
	$c = q('SELECT img, id FROM '.$tbl.'smiley ORDER BY vieworder');
	while ($r = db_rowarr($c)) {
		if (!@file_exists($WWW_ROOT_DISK . 'images/smiley_icons/' . $r[0])) {
			++$cnt;
			q('DELETE FROM '.$tbl.'smiley WHERE id='.$r[1]);
		}
		$sml[$r[0]] = 1;
		q('UPDATE '.$tbl.'smiley SET vieworder='.(++$i).' WHERE id='.$r[1]);
	}
	unset($c);
	draw_info($cnt);

	draw_stat(lang('Checking disk files against smilies'));
	$cnt = 0;
	$dp = opendir($WWW_ROOT_DISK . 'images/smiley_icons');
	readdir($dp); readdir($dp);
	while ($f = readdir($dp)) {
		if (!isset($sml[$f]) && !preg_match('!\.(gif|png|jpg|jpeg|html)$!i', $f)) {
			if (@unlink($WWW_ROOT_DISK . 'images/smiley_icons/' . $f)) {
				draw_stat(lang('deleted smiley: %1', $f));
				++$cnt;
			} else {
				draw_info(lang('Unable to delete smiley: %1', $f));
			}
		}
	}
	closedir($dp);
	unset($sml);
	draw_info($cnt);

	draw_stat(lang('Checking topic notification'));
	delete_zero($tbl.'thread_notify', 'SELECT tn.id FROM '.$tbl.'thread_notify tn LEFT JOIN '.$tbl.'thread t ON t.id=tn.thread_id LEFT JOIN '.$tbl.'users u ON u.id=tn.user_id WHERE u.id IS NULL OR t.id IS NULL');

	draw_stat(lang('Checking forum notification'));
	delete_zero($tbl.'forum_notify', 'SELECT fn.id FROM '.$tbl.'forum_notify fn LEFT JOIN '.$tbl.'forum f ON f.id=fn.forum_id LEFT JOIN '.$tbl.'users u ON u.id=fn.user_id WHERE u.id IS NULL OR f.id IS NULL');

	draw_stat(lang('Checking topic votes against topics'));
	delete_zero($tbl.'thread_rate_track', 'SELECT trt.id FROM '.$tbl.'thread_rate_track trt LEFT JOIN '.$tbl.'thread t ON t.id=trt.thread_id LEFT JOIN '.$tbl.'users u ON u.id=trt.user_id WHERE u.id IS NULL OR t.id IS NULL');

	draw_stat(lang('Rebuild topic rating cache'));
	q('UPDATE '.$tbl.'thread SET rating=0, n_rating=0');
	$c = q('SELECT thread_id, count(*), AVG(rating) FROM '.$tbl.'thread_rate_track GROUP BY thread_id');
	while ($r = db_rowarr($c)) {
		q('UPDATE '.$tbl.'thread SET rating='.round($r[2]).', n_rating='.(int)$r[1].' WHERE id='.$r[0]);
	}
	unset($c);
	draw_stat(lang('Done').': '.lang('Rebuild topic rating cache'));

	draw_stat(lang('Rebuilding Topic Views'));
	q('DELETE FROM '.$tbl.'thread_view');
	$c = q('SELECT id FROM '.$tbl.'forum');
	while ($r = db_rowarr($c)) {
		rebuild_forum_view($r[0]);
	}
	unset($fr);
	draw_stat(lang('Done').': '.lang('Rebuilding Topic Views'));

	draw_stat(lang('Rebuilding user ranks, message counts & last post ids'));
	q('UPDATE '.$tbl.'users SET level_id=0, posted_msg_count=0, u_last_post_id=0, custom_status=NULL');
	if (__dbtype__ == 'mysql') {
		q('INSERT INTO '.$tbl.'tmp_consist (ps, p, c) SELECT MAX(post_stamp), poster_id, count(*) FROM '.$tbl.'msg WHERE apr=1 GROUP BY poster_id ORDER BY poster_id');
		$c = q('SELECT '.$tbl.'tmp_consist.p, '.$tbl.'tmp_consist.c, m.id FROM '.$tbl.'tmp_consist INNER JOIN '.$tbl.'msg m ON m.apr=1 AND m.poster_id='.$tbl.'tmp_consist.p AND m.post_stamp='.$tbl.'tmp_consist.ps');
		while ($r = db_rowarr($c)) {
			if (!$r[1]) { continue; }
			q('UPDATE '.$tbl.'users SET u_last_post_id='.$r[2].', posted_msg_count='.$r[1].' WHERE id='.$r[0]);
		}
		unset($c);
	} else {
		$c = q('SELECT MAX(post_stamp), poster_id, count(*) FROM '.$tbl.'msg WHERE apr=1 GROUP BY poster_id ORDER BY poster_id');
		while (list($ps, $uid, $cnt) = db_rowarr($c)) {
			if (!$uid) { continue; }
			q('UPDATE '.$tbl.'users SET posted_msg_count='.$cnt.', u_last_post_id=(SELECT id FROM '.$tbl.'msg WHERE post_stamp='.$ps.' AND apr=1 AND poster_id='.$uid.') WHERE id='.$uid);
		}
		unset($c);
	}

	$c = q('SELECT id, post_count FROM '.$tbl.'level ORDER BY post_count DESC');
	while ($r = db_rowarr($c)) {
		q('UPDATE '.$tbl.'users SET level_id='.$r[0].' WHERE level_id=0 AND posted_msg_count>='.$r[1]);
	}
	unset($c);

	draw_stat(lang('Done').': '.lang('Rebuilding user levels, message counts & last post ids'));

	draw_stat(lang('Checking buddy list entries'));
	delete_zero($tbl.'buddy', 'SELECT b.id FROM '.$tbl.'buddy b LEFT JOIN '.$tbl.'users u1 ON u1.id=b.user_id LEFT JOIN '.$tbl.'users u2 ON u2.id=b.bud_id WHERE u1.id IS NULL OR u2.id IS NULL');

	draw_stat(lang('Checking ignore list entries'));
	delete_zero($tbl.'user_ignore', 'SELECT i.id FROM '.$tbl.'user_ignore i LEFT JOIN '.$tbl.'users u1 ON u1.id=i.user_id LEFT JOIN '.$tbl.'users u2 ON u2.id=i.ignore_id WHERE u1.id IS NULL OR u2.id IS NULL');

	// we do this together to avoid dupe query
	q('UPDATE '.$tbl.'users SET buddy_list=NULL, ignore_list=NULL');

	draw_stat(lang('Rebuilding buddy list cache'));
	$oldu = '';
	$br = array();
	$c = q('SELECT bud_id, user_id FROM '.$tbl.'buddy ORDER BY user_id');
	while ($r = db_rowarr($c)) {
		if ($oldu != $r[1]) {
			if ($oldu) {
				q('UPDATE '.$tbl.'users SET buddy_list='.strnull(addslashes(@serialize($br))).' WHERE id='.$oldu);
				$br = array();
			}
			$oldu = $r[1];
		}
		$br[$r[0]] = 1;
	}
	unset($c);
	if (count($br)) {
		q('UPDATE '.$tbl.'users SET buddy_list='.strnull(addslashes(@serialize($br))).' WHERE id='.$oldu);
		unset($br);
	}
	draw_stat(lang('Done').': '.lang('Rebuilding buddy list cache'));

	draw_stat(lang('Rebuilding ignore list cache'));
	$oldu = '';
	$ir = array();
	$c = q('SELECT ignore_id, user_id FROM '.$tbl.'user_ignore ORDER BY user_id');
	while ($r = db_rowarr($c)) {
		if ($oldu != $r[1]) {
			if ($oldu) {
				q('UPDATE '.$tbl.'users SET ignore_list='.strnull(addslashes(@serialize($ir))).' WHERE id='.$oldu);
				$bi = array();
			}
			$oldu = $r[1];
		}
		$ir[$r[0]] = 1;
	}
	unset($c);
	if (count($ir)) {
		q('UPDATE '.$tbl.'users SET ignore_list='.strnull(addslashes(@serialize($ir))).' WHERE id='.$oldu);
		unset($ir);
	}
	draw_stat(lang('Done').': '.lang('Rebuilding ignore list cache'));

	draw_stat(lang('Rebuilding ip filter cache'));
	ip_cache_rebuild();
	draw_stat(lang('Done').': '.lang('Rebuilding ip filter cache'));

	draw_stat(lang('Rebuilding login filter cache'));
	login_cache_rebuild();
	draw_stat(lang('Done').': '.lang('Rebuilding login filter cache'));

	draw_stat(lang('Rebuilding email filter cache'));
	email_cache_rebuild();
	draw_stat(lang('Done').': '.lang('Rebuilding email filter cache'));

	draw_stat(lang('Rebuilding extension filter cache'));
	ext_cache_rebuild();
	draw_stat(lang('Done').': '.lang('Rebuilding extension filter cache'));

	draw_stat(lang('Rebuilding custom tags for users'));
	$c = q('SELECT distinct(user_id) FROM '.$tbl.'custom_tags');
	while ($r = db_rowarr($c)) {
		ctag_rebuild_cache($r[0]);
	}
	unset($c);
	draw_stat(lang('Done').': '.lang('Rebuilding custom tags for users'));

	draw_stat(lang('Validating group resources'));
	delete_zero($tbl.'group_resources', 'SELECT gr.id FROM '.$tbl.'group_resources gr LEFT JOIN '.$tbl.'forum f ON f.id=gr.resource_id LEFT JOIN '.$tbl.'groups g ON g.id=gr.group_id WHERE f.id IS NULL OR g.id IS NULL');
	draw_stat(lang('Done').': '.lang('Validating group resources'));

	draw_stat(lang('Validating group validity'));
	# technically a group cannot exist without being assigned to at least 1 resource
	# so when we encounter such as group, we do our patriotic duty and remove it.
	delete_zero($tbl.'groups', 'SELECT g.id FROM '.$tbl.'groups g LEFT JOIN '.$tbl.'group_resources gr ON g.id=gr.group_id WHERE g.id > 2 AND gr.id IS NULL');
	draw_stat(lang('Done').': '.lang('Validating group validity'));

	draw_stat(lang('Validating group members'));
	delete_zero($tbl.'group_members', 'SELECT gm.id FROM '.$tbl.'group_members gm LEFT JOIN '.$tbl.'users u ON u.id=gm.user_id LEFT JOIN '.$tbl.'groups g ON g.id=gm.group_id WHERE (u.id IS NULL AND gm.user_id NOT IN(0, 2147483647)) OR g.id IS NULL');
	draw_stat(lang('Done').': '.lang('Validating group members'));

	draw_stat(lang('Validating group/forum relations'));
	$c = q('SELECT f.id, f.name FROM '.$tbl.'forum f LEFT JOIN '.$tbl.'groups g ON f.id=g.forum_id WHERE g.id IS NULL');
	while ($r = db_rowarr($c)) {
		group_add($r[0], $r[1], 2);
	}
	unset($c);
	draw_stat(lang('Done').': '.lang('Validating group/forum relations'));

	draw_stat(lang('Validating group/forum names'));
	$c = q('SELECT g.id, f.name FROM '.$tbl.'groups g INNER JOIN '.$tbl.'forum f ON f.id=g.forum_id WHERE g.id>2 AND f.name!=g.name');
	$i = 0;
	while ($r = db_rowarr($c)) {
		q("UPDATE ".$tbl."groups SET name='".addslashes($r[1])."' WHERE id=".$r[0]);
		++$i;
	}
	unset($r);
	draw_stat(lang('Done').': '.lang('Validating group/forum names (fixed: %1 relations)', $i));

	draw_stat(lang('Validating group/primary user relations'));
	$c = uq('SELECT g.id, gm1.id, gm2.id FROM '.$tbl.'groups g LEFT JOIN '.$tbl.'group_members gm1 ON gm1.group_id=g.id AND gm1.user_id=0 LEFT JOIN '.$tbl.'group_members gm2 ON gm2.group_id=g.id AND gm2.user_id=2147483647 WHERE g.id>2 AND g.forum_id>0 AND (gm1.id IS NULL OR gm2.id IS NULL)');
	while ($r = db_rowarr($c)) {
		if (!$r[1]) {
			$glm[$r[0]][] = 0;
		}
		if (!$r[2]) {
			$glm[$r[0]][] = 2147483647;
		}
	}
	if (isset($glm)) {
		// make group based on 'primary' 1st group
		$fld_lst = implode(',', $GLOBALS['__GROUPS_INC']['permlist']);
		$anon = "'" . implode("', '", db_arr_assoc('SELECT '.$fld_lst.' FROM '.$tbl.'groups WHERE id=1')) . "'";
		$regu = "'" . implode("', '", db_arr_assoc('SELECT '.$fld_lst.' FROM '.$tbl.'groups WHERE id=2')) . "'";
		$fld_lst = str_replace('p_', 'up_', $fld_lst);
		foreach ($glm as $k => $v) {
			foreach ($v as $uid) {
				q('INSERT INTO '.$tbl.'group_members (group_id, user_id, '.$fld_lst.') VALUES ('.$k.', '.$uid.', '.(!$uid ? $anon : $regu).')');
			}
		}
	}
	draw_stat(lang('Done').': '.lang('Validating group/primary user relations'));

	draw_stat(lang('Rebuilding group leader cache'));
	$c = q('SELECT DISTINCT(user_id) FROM '.$tbl.'group_members WHERE group_members_opt>=131072 AND (group_members_opt & 131072) > 0');
	while ($r = db_rowarr($c)) {
		rebuild_group_ldr_cache($r[0]);
	}
	unset($c);
	draw_stat(lang('Done').': '.lang('Rebuilding group leader cache'));

	draw_stat(lang('Rebuilding group cache'));
	grp_rebuild_cache();
	draw_stat(lang('Done').': '.lang('Rebuilding group cache'));

	draw_stat(lang('Validating User/Theme Relations'));
	$te = array();
	$c = uq('SELECT u.id FROM '.$tbl.'users u LEFT JOIN '.$tbl.'themes thm ON thm.id=u.theme WHERE thm.id IS NULL');
	while (list($uid) = db_rowarr($c)) {
		$te[] = $uid;
	}
	if ($te) {
		$tid = q_singleval('SELECT id FROM '.$tbl.'themes WHERE theme_opt=3');
		q('UPDATE '.$tbl.'users SET theme='.$tid.' WHERE id IN('.implode(',', $te).')');
	}
	draw_stat(lang('Done').': '.lang('Validating User/Theme Relations'));

	draw_stat(lang('Rebuilding Forum/Category order cache'));
	rebuild_forum_cat_order();
	draw_stat(lang('Done').': '.lang('Rebuilding Forum/Category order cache'));

	draw_stat(lang('Unlocking database'));
	db_unlock();
	if (__dbtype__ == 'mysql') {
		q('DROP TABLE '.$tbl.'tmp_consist');
	}
	draw_stat(lang('Database unlocked'));

	draw_stat(lang('Cleaning forum\'s tmp directory'));
	if (($d = opendir($TMP))) {
		readdir($d); readdir($d);
		while ($f = readdir($d)) {
			if (@is_file($TMP . $f)) {
				@unlink($TMP . $f);
			}
		}
		closedir($d);
	}
	draw_stat(lang('Done').': '.lang('Cleaning forum\'s tmp directory'));

	draw_stat(lang('Validate GLOBALS.php'));
	$gvars = array();
	$data = file($GLOBALS['INCLUDE'] . 'GLOBALS.php');
	$olc = count($data);
	foreach ($data as $k => $l) {
		if (($p = strpos($l, '$')) !== false) {
			++$p;
			if (($e = strpos($l, '=', $p)) !== false) {
				$var = rtrim(substr($l, $p, ($e - $p)));
				if (isset($gvars[$var])) {
					unset($data[$k]);
				} else {
					$gvars[$var] = 1;
				}
			}
		} else if (!trim($l)) {
			unset($data[$k]);
		}
	}
	if ($olc != count($data)) {
		$fp = fopen($GLOBALS['INCLUDE'] . 'GLOBALS.php', 'w');
		fwrite($fp, implode('', $data));
		if (strpos(array_pop($data), '?>') === false) {
			fwrite($fp, "\n?>");
		}
		fclose($fp);
	}
	draw_stat(lang('Done').': '.lang('Validate GLOBALS.php'));

	if ($FUD_OPT_1 & 1 || isset($_GET['enable_forum'])) {
		draw_stat(lang('Re-enabling the forum.'));
		maintenance_status($DISABLED_REASON, 0);
	} else {
		echo '<font size="+1" color="red">'.lang('Your forum is currently disabled, to re-enable it go to the <a href="admglobal.php?%1">Global Settings Manager</a> and re-enable it.',_rsid).'</font><br>';
	}

	draw_stat(lang('DONE'));

	echo lang('It is recommended that you run SQL table optimizer after completing the consistency check. To do so <a href="consist.php?opt=1&%1">click here</a>, keep in mind that this process make take several minutes to perform.',_rsid);
	echo '<script language="Javascript1.2">clearInterval(intervalID);</script>';
	require($WWW_ROOT_DISK . 'adm/admclose.php');
?>
