<?php
//
//	file: includes/acp/acp_attach_maintain.php
//	author: ptirhiik
//	begin: 25/02/2006
//	version: 1.6.0 - 10/06/2006
//	license: http://opensource.org/licenses/gpl-license.php GNU General Public License (GPL)
//
//	based on : attachment_mod copyright (c) 2002 Meik Sievertsen (acydburn)
//

if ( !defined('IN_PHPBB') )
{
	die('Hack attempt');
}

define('ROWS_A_TURN', 100);

$cp_title = 'Attach_maintain';
$cp_title_explain = 'Attach_maintain_explain';

function redirect_sub($url)
{
	global $config;
	if ( defined('IN_ADMIN') )
	{
		redirect(substr($url, strlen(preg_replace('#^\.\/#', '', $config->root))));
	}
	else
	{
		redirect($url);
	}
}

class attach_maintain
{
	var $requester;
	var $parms;
	var $search_id;
	var $act;
	var $actions;

	var $handler;

	function attach_maintain($requester, $parms='')
	{
		global $attach_config;

		$this->requester = $requester;
		$this->parms = empty($parms) ? array() : $parms;
		$this->handler = new file_handler($attach_config->data);

		$this->search_id = 0;
		$this->act = '';
		$this->actions = array(
			'renew' => array('legend' => 'Renew_directory', 'explain' => 'Renew_directory_explain', 'img' => 'cmd_regen'),
			'unknown' => array('legend' => 'Delete_unknown', 'explain' => 'Delete_unknown_explain', 'img' => 'cmd_delete', 'search_id' => true),
			'deleted' => array('legend' => 'Delete_deleted', 'explain' => 'Delete_deleted_explain', 'img' => 'cmd_delete', 'search_id' => true),
			'dead' => array('legend' => 'Delete_dead', 'explain' => 'Delete_dead_explain', 'img' => 'cmd_delete'),
			'orphan' => array('legend' => 'Delete_orphan', 'explain' => 'Delete_orphan_explain', 'img' => 'cmd_delete'),
			'presync' => array('legend' => 'Resync_posts', 'explain' => 'Resync_posts_explain', 'img' => 'cmd_synchro'),
			'tresync' => array('legend' => 'Resync_topics', 'explain' => 'Resync_topics_explain', 'img' => 'cmd_synchro'),
		);
	}

	function process()
	{
		if ( $this->init() )
		{
			$this->check();
			$this->validate();
			$this->display();
		}
	}

	function init()
	{
		$this->search_id = _read('search_id', TYPE_INT);
		$this->act = _read('act', TYPE_NO_HTML, '', array('' => '') + $this->actions);
		return true;
	}

	function check()
	{
	}

	function validate()
	{
		switch ( $this->act )
		{
			case 'renew':
				if ( $this->search_id && !_read('thumb', TYPE_INT) )
				{
					return;
				}
				$this->read_dir();
				break;
			case 'unknown':
				$this->prune_unknown();
				break;
			case 'deleted':
				$this->prune_deleted();
				break;
			case 'dead':
				$this->prune_dead();
				break;
			case 'orphan':
				$this->prune_orphan();
				break;
			case 'presync':
				$this->resync_posts();
				break;
			case 'tresync':
				$this->resync_topics();
				break;
		}
	}

	function display()
	{
		global $template, $config, $user;
		global $cp_title;

		$actions = $this->actions;
		if ( $this->search_id )
		{
			unset($actions['renew']);
		}
		else
		{
			unset($actions['unknown']);
			unset($actions['deleted']);
		}

		// send a menu
		$template->assign_vars(array(
			'L_BOX' => $user->lang($cp_title),
		));
		$count_actions = count($actions);
		$i = 0;
		foreach ( $actions as $action => $data )
		{
			$template->assign_block_vars('action', array(
				'U_ACTION' => $config->url($this->requester, $this->parms + array('act' => $action) + (empty($this->search_id) || !$data['search_id'] ? array() : array('search_id' => $this->search_id)), true),
				'I_ACTION' => $user->img($data['img']),
				'L_ACTION' => $user->lang($data['legend']),
				'L_ACTION_EXPLAIN' => $user->lang($data['explain']),
			));
			$i++;
			$template->set_switch('action.last', $i == $count_actions);
		}
		$template->assign_block_vars('cp_content', array('BOX' => $template->include_file('acp/acp_attach_maintain_box.tpl')));
	}

	function progress_bar($title, $defs, $parms)
	{
		global $template, $user, $config;

		$template->assign_block_vars('progress', array(
			'L_PROGRESS' => $user->lang($title),
		));
		$parms['act'] = $this->act;

		$loop = false;
		foreach ( $defs as $sub_title => $def )
		{
			$total = intval($parms[ $def['total'] ]);
			$done = intval($parms[ $def['done'] ]);
			$percent_done = $total == 0 ? 100 : max(1, min(100, ceil(100 * $done / $total)));
			$mult = 3;
			$template->assign_block_vars('progress.row', array(
				'WIDTH_TOTAL' => 100 * $mult,
				'WIDTH_DONE' => $percent_done * $mult,
				'L_RESULTS' => sprintf($user->lang($sub_title . ($done < $total ? '_progress' : '_achieved')), $done, $total),
				'L_COMMENT' => $user->lang($sub_title . '_comment'),
			));
			$loop |= $done < $total;
		}
		if ( $loop )
		{
			$template->assign_vars(array(
				'META' => '<meta http-equiv="refresh" content="0;url=' . $config->url($this->requester, $this->parms + $parms, true) . '">',
			));
		}
	}

	function read_dir()
	{
		global $user, $config, $db;

		$thumb = _read('thumb', TYPE_INT);

		// get an id
		if ( !$this->search_id )
		{
			list($sec, $usec) = explode(' ', microtime());
			mt_srand((float) $sec + ((float) $usec * 100000));
			$this->search_id = mt_rand();

			// delete previous results
			$sql = 'TRUNCATE TABLE ' . ATT_TEMP_DIR_TABLE;
			$db->sql_query($sql, false, __LINE__, __FILE__);
		}

		// upload per move/copy
		$files = array();
		$this->handler->open_channel();
		$this->handler->read_channel($thumb ? THUMB_DIR : '', $files, $thumb ? 't_' : '');
		$this->handler->close_channel();
		if ( empty($files) && !$thumb )
		{
			message_die(GENERAL_ERROR, 'Error_no_files_found');
		}

		$count_files = count($files);
		$db->sql_stack_reset();
		for ( $i = 0; $i < $count_files; $i++ )
		{
			$fields = array(
				'search_id' => $this->search_id,
				'thumbnail' => $thumb ? 1 : 0,
				'filename' => $files[$i],
			);
			$db->sql_stack_statement($fields);
			if ( !($i % ROWS_A_TURN) )
			{
				$db->sql_stack_insert(ATT_TEMP_DIR_TABLE, false, __LINE__, __FILE__);
				$db->sql_stack_reset();
			}
		}
		unset($files);
		if ( $count_files )
		{
			$db->sql_stack_insert(ATT_TEMP_DIR_TABLE, false, __LINE__, __FILE__);
		}
		if ( !$thumb )
		{
			redirect_sub($config->url($this->requester, $this->parms + array('search_id' => $this->search_id, 'act' => 'renew', 'thumb' => true), true));
		}
		else
		{
			redirect_sub($config->url($this->requester, $this->parms + array('search_id' => $this->search_id), true));
		}
	}

	function prune_unknown()
	{
		global $db, $config;

		// process step
		$step = _read('step', TYPE_INT);

		// files without attachments
		$total = _read('total', TYPE_INT);
		$done = _read('done', TYPE_INT);

		// thumb files without main files
		$total_thb = _read('total_thb', TYPE_INT);
		$done_thb = _read('done_thb', TYPE_INT);

		// thumbnails lost (deleted in attach desc)
		$total_lost = _read('total_lost', TYPE_INT);
		$done_lost = _read('done_lost', TYPE_INT);

		switch ( $step )
		{
			// get totals
			case 0:
				// total mains without attach
				$sql = 'SELECT r.filename
							FROM ' . ATT_TEMP_DIR_TABLE . ' r
								LEFT JOIN ' . ATT_DESC_TABLE . ' d
									ON d.physical_filename = r.filename
							WHERE d.physical_filename IS NULL
								AND search_id = ' . intval($this->search_id) . '
							GROUP BY r.filename';
				$result = $db->sql_query($sql, false, __LINE__, __FILE__);
				$total = $db->sql_numrows($result);
				$db->sql_freeresult($result);
				$done = 0;

				// total thumbs without mains
				$sql = 'SELECT rt.filename
							FROM ' . ATT_TEMP_DIR_TABLE . ' rt
								LEFT JOIN ' . ATT_TEMP_DIR_TABLE . ' r
									ON r.search_id = rt.search_id
										AND r.filename = rt.filename
										AND r.thumbnail = 0
							WHERE rt.search_id = ' . intval($this->search_id) . '
								AND rt.thumbnail = 1
								AND r.filename IS NULL';
				$result = $db->sql_query($sql, false, __LINE__, __FILE__);
				$total_thb = $db->sql_numrows($result);
				$db->sql_freeresult($result);
				$done_thb = 0;

				// total thumbs lost
				$sql = 'SELECT r.filename
							FROM ' . ATT_TEMP_DIR_TABLE . ' r, ' . ATT_DESC_TABLE . ' d
							WHERE r.search_id = ' . intval($this->search_id) . '
								AND r.thumbnail = 1
								AND d.physical_filename = r.filename
								AND d.thumbnail = 0';
				$result = $db->sql_query($sql, false, __LINE__, __FILE__);
				$total_lost = $db->sql_numrows($result);
				$db->sql_freeresult($result);
				$done_lost = 0;

				$step++;
				break;

			// mains without attach
			case 1:
				if ( $total )
				{
					$files_list = array();
					$sql = 'SELECT r.filename, MIN(r.thumbnail) AS thumb_only, MAX(r.thumbnail) AS has_thumb
								FROM ' . ATT_TEMP_DIR_TABLE . ' r
									LEFT JOIN ' . ATT_DESC_TABLE . ' d
										ON d.physical_filename = r.filename
								WHERE d.physical_filename IS NULL
									AND search_id = ' . intval($this->search_id) . '
								GROUP BY r.filename
								LIMIT ' . ROWS_A_TURN;
					$result = $db->sql_query($sql, false, __LINE__, __FILE__);
					if ( $todo = $db->sql_numrows($result) )
					{
						$this->handler->open_channel();
						while ( $row = $db->sql_fetchrow($result) )
						{
							$this->handler->unlink($row['filename'], $row['has_thumb'], $row['thumb_only']);
							$files_list[] = $db->sql_escape_string($row['filename']);
						}
						$this->handler->close_channel();
					}
					$db->sql_freeresult($result);

					// delete records
					if ( $count_files_list = count($files_list) )
					{
						$sql = 'DELETE FROM ' . ATT_TEMP_DIR_TABLE . '
									WHERE filename IN(\'' . implode('\', \'', $files_list) . '\')
										AND search_id = ' . intval($this->search_id);
						$db->sql_query($sql, false, __LINE__, __FILE__);
					}
					unset($files_list);

					// count
					$done = $todo ? min($total, $done + $todo) : $total;

					// this step over ?
					if ( $done >= $total )
					{
						$step++;
					}
					break;
				}
				else
				{
					$step++;
				}

			// thumbs without mains
			case 2:
				if ( $total_thb )
				{
					$thumbs_list = array();
					$sql = 'SELECT rt.filename
								FROM ' . ATT_TEMP_DIR_TABLE . ' rt
									LEFT JOIN ' . ATT_TEMP_DIR_TABLE . ' r
										ON r.search_id = rt.search_id
											AND r.filename = rt.filename
											AND r.thumbnail = 0
								WHERE rt.search_id = ' . intval($this->search_id) . '
									AND rt.thumbnail = 1
									AND r.filename IS NULL
								LIMIT ' . ROWS_A_TURN;
					$result = $db->sql_query($sql, false, __LINE__, __FILE__);
					if ( $todo = $db->sql_numrows($result) )
					{
						$this->handler->open_channel();
						while ( $row = $db->sql_fetchrow($result) )
						{
							$this->handler->unlink($row['filename'], true, true);
							$thumbs_list[] = $db->sql_escape_string($row['filename']);
						}
						$this->handler->close_channel();
					}
					$db->sql_freeresult($result);

					// delete records
					if ( $count_thumbs_list = count($thumbs_list) )
					{
						$sql = 'DELETE FROM ' . ATT_TEMP_DIR_TABLE . '
									WHERE filename IN(\'' . implode('\', \'', $thumbs_list) . '\')
										AND thumbnail = 1
										AND search_id = ' . intval($this->search_id);
						$db->sql_query($sql, false, __LINE__, __FILE__);
					}
					unset($thumbs_list);

					// count
					$done_thb = $todo ? min($total_thb, $done_thb + $todo) : $total_thb;

					// this step over ?
					if ( $done_thb >= $total_thb )
					{
						$step++;
					}
					break;
				}
				else
				{
					$step++;
				}

			// thumbs lost
			case 3:
				if ( $total_lost )
				{
					$thumbs_list = array();
					$sql = 'SELECT r.filename
								FROM ' . ATT_TEMP_DIR_TABLE . ' r, ' . ATT_DESC_TABLE . ' d
								WHERE r.search_id = ' . intval($this->search_id) . '
									AND r.thumbnail = 1
									AND d.physical_filename = r.filename
									AND d.thumbnail = 0
								LIMIT ' . ROWS_A_TURN;
					$result = $db->sql_query($sql, false, __LINE__, __FILE__);
					if ( $todo = $db->sql_numrows($result) )
					{
						$this->handler->open_channel();
						while ( $row = $db->sql_fetchrow($result) )
						{
							$this->handler->unlink($row['filename'], true, true);
							$thumbs_list[] = $db->sql_escape_string($row['filename']);
						}
						$this->handler->close_channel();
					}
					$db->sql_freeresult($result);

					// delete records
					if ( $count_thumbs_list = count($thumbs_list) )
					{
						$sql = 'DELETE FROM ' . ATT_TEMP_DIR_TABLE . '
									WHERE filename IN(\'' . implode('\', \'', $thumbs_list) . '\')
										AND thumbnail = 1
										AND search_id = ' . intval($this->search_id);
						$db->sql_query($sql, false, __LINE__, __FILE__);
					}
					unset($thumbs_list);

					// count
					$done_lost = $todo ? min($total_lost, $done_lost + $todo) : $total_lost;

					// this step over ?
					if ( $done_lost >= $total_lost )
					{
						$step++;
					}
					break;
				}
				else
				{
					$step++;
				}
		}

		// send a progress bar
		$defs = array(
			'Delete_unknown' => array('done' => 'done', 'total' => 'total'),
			'Delete_unknown_thb' => array('done' => 'done_thb', 'total' => 'total_thb'),
			'Delete_unknown_lost' => array('done' => 'done_lost', 'total' => 'total_lost'),
		);
		$parms = array(
			'search_id' => $this->search_id,
			'step' => $step,
			'total' => $total,
			'done' => $done,
			'total_thb' => $total_thb,
			'done_thb' => $done_thb,
			'total_lost' => $total_lost,
			'done_lost' => $done_lost,
		);
		$this->progress_bar('Delete_unknown', $defs, $parms);
	}

	function prune_deleted()
	{
		global $db, $config;

		// process step
		$step = _read('step', TYPE_INT);

		// attachments desc without files
		$total = _read('total', TYPE_INT);
		$done = _read('done', TYPE_INT);

		// thumbnails desc without thumbnail files
		$total_thb = _read('total', TYPE_INT);
		$done_thb = _read('done', TYPE_INT);

		switch ( $step )
		{
			// get totals
			case 0:
				// get total of attachments desc without file
				$sql = 'SELECT d.attach_id
							FROM ' . ATT_DESC_TABLE . ' d
								LEFT JOIN ' . ATT_TEMP_DIR_TABLE . ' r
									ON r.filename = d.physical_filename
										AND r.thumbnail = 0
										AND r.search_id = ' . intval($this->search_id) . '
							WHERE r.filename IS NULL';
				$result = $db->sql_query($sql, false, __LINE__, __FILE__);
				$total = $db->sql_numrows($result);
				$db->sql_freeresult($result);
				$done = 0;

				// get total thumbnail desc without thumbnails files, minus the first
				$sql = 'SELECT d.attach_id
							FROM ' . ATT_DESC_TABLE . ' d
								LEFT JOIN ' . ATT_TEMP_DIR_TABLE . ' r
									ON r.filename = d.physical_filename
										AND r.thumbnail = 1
										AND r.search_id = ' . intval($this->search_id) . '
							WHERE d.thumbnail = 1
								AND r.filename IS NULL';
				$result = $db->sql_query($sql, false, __LINE__, __FILE__);
				$total_thb = max(0, $db->sql_numrows($result) - $total);
				$db->sql_freeresult($result);
				$done_thb = 0;

				$step++;
				break;

			// attachments desc without file
			case 1:
				if ( $total )
				{
					$attach_ids = array();
					$sql = 'SELECT d.attach_id
								FROM ' . ATT_DESC_TABLE . ' d
									LEFT JOIN ' . ATT_TEMP_DIR_TABLE . ' r
										ON r.filename = d.physical_filename
											AND r.thumbnail = 0
											AND r.search_id = ' . intval($this->search_id) . '
								WHERE r.filename IS NULL
								LIMIT ' . ROWS_A_TURN;
					$result = $db->sql_query($sql, false, __LINE__, __FILE__);
					if ( $todo = $db->sql_numrows($result) )
					{
						while ( $row = $db->sql_fetchrow($result) )
						{
							$attach_ids[] = intval($row['attach_id']);
						}
					}
					$db->sql_freeresult($result);

					if ( $count_attach_ids = count($attach_ids) )
					{
						$sql = 'DELETE FROM ' . ATT_DESC_TABLE . '
									WHERE attach_id IN(' . implode(', ', $attach_ids) . ')';
						$db->sql_query($sql, false, __LINE__, __FILE__);
					}
					unset($attach_ids);

					// count
					$done = $todo ? min($total, $done + $todo) : $total;

					// this step over ?
					if ( $done >= $total )
					{
						$step++;
					}
					break;
				}
				else
				{
					$step++;
				}

			// thumbnail desc without thumbnails files
			case 2:
				if ( $total_thb )
				{
					$attach_ids = array();
					$sql = 'SELECT d.attach_id
								FROM ' . ATT_DESC_TABLE . ' d
									LEFT JOIN ' . ATT_TEMP_DIR_TABLE . ' r
										ON r.filename = d.physical_filename
											AND r.thumbnail = 1
											AND r.search_id = ' . intval($this->search_id) . '
								WHERE d.thumbnail = 1
									AND r.filename IS NULL
								LIMIT ' . ROWS_A_TURN;
					$result = $db->sql_query($sql, false, __LINE__, __FILE__);
					if ( $todo = $db->sql_numrows($result) )
					{
						while ( $row = $db->sql_fetchrow($result) )
						{
							$attach_ids[] = intval($row['attach_id']);
						}
					}
					$db->sql_freeresult($result);

					if ( $count_attach_ids = count($attach_ids) )
					{
						$sql = 'UPDATE ' . ATT_DESC_TABLE . '
									SET thumbnail = 0
									WHERE attach_id IN(' . implode(', ', $attach_ids) . ')';
						$db->sql_query($sql, false, __LINE__, __FILE__);
					}
					unset($attach_ids);

					// count
					$done_thb = $todo ? min($total_thb, $done_thb + $todo) : $total_thb;

					// this step over ?
					if ( $done_thb >= $total_thb )
					{
						$step++;
					}
					break;
				}
				else
				{
					$step++;
				}
		}

		// send a progress bar
		$defs = array(
			'Delete_deleted' => array('done' => 'done', 'total' => 'total'),
			'Delete_deleted_thb' => array('done' => 'done_thb', 'total' => 'total_thb'),
		);
		$parms = array(
			'search_id' => $this->search_id,
			'step' => $step,
			'total' => $total,
			'done' => $done,
			'total_thb' => $total_thb,
			'done_thb' => $done_thb,
		);
		$this->progress_bar('Delete_deleted', $defs, $parms);
	}

	function prune_dead()
	{
		global $db, $config;

		// process step
		$step = _read('step', TYPE_INT);

		// attachments without posts
		$total = _read('total', TYPE_INT);
		$done = _read('done', TYPE_INT);

		// attachments without pms
		$total_pm = _read('total_pm', TYPE_INT);
		$done_pm = _read('done_pm', TYPE_INT);

		switch ( $step )
		{
			// get totals
			case 0:
				// get total of attachments without posts
				$sql = 'SELECT a.attach_id
							FROM ' . ATT_LINKS_TABLE . ' a
								LEFT JOIN ' . POSTS_TABLE . ' p
									ON p.post_id = a.post_id
							WHERE a.post_id <> 0
								AND p.post_id IS NULL';
				$result = $db->sql_query($sql, false, __LINE__, __FILE__);
				$total = $db->sql_numrows($result);
				$db->sql_freeresult($result);
				$done = 0;

				// get total of attachments without pms
				$sql = 'SELECT a.attach_id
							FROM ' . ATT_LINKS_TABLE . ' a
								LEFT JOIN ' . PRIVMSGS_TABLE . ' p
									ON p.privmsgs_id = a.privmsgs_id
							WHERE a.privmsgs_id <> 0
								AND p.privmsgs_id IS NULL';
				$result = $db->sql_query($sql, false, __LINE__, __FILE__);
				$total_pm = $db->sql_numrows($result);
				$db->sql_freeresult($result);
				$done_pm = 0;

				$step++;
				break;

			// attachments without posts
			case 1:
				if ( $total )
				{
					$attach_ids = array();
					$sql = 'SELECT a.attach_id
								FROM ' . ATT_LINKS_TABLE . ' a
									LEFT JOIN ' . POSTS_TABLE . ' p
										ON p.post_id = a.post_id
								WHERE a.post_id <> 0
									AND p.post_id IS NULL
								LIMIT ' . ROWS_A_TURN;
					$result = $db->sql_query($sql, false, __LINE__, __FILE__);
					if ( $todo = $db->sql_numrows($result) )
					{
						while ( $row = $db->sql_fetchrow($result) )
						{
							$attach_ids[] = intval($row['attach_id']);
						}
					}
					$db->sql_freeresult($result);

					if ( $count_attach_ids = count($attach_ids) )
					{
						$sql = 'DELETE FROM ' . ATT_LINKS_TABLE . '
									WHERE attach_id IN(' . implode(', ', $attach_ids) . ')';
						$db->sql_query($sql, false, __LINE__, __FILE__);
					}
					unset($attach_ids);

					// count
					$done = $todo ? min($total, $done + $todo) : $total;

					// this step over ?
					if ( $done >= $total )
					{
						$step++;
					}
					break;
				}
				else
				{
					$step++;
				}

			// attachments without pms
			case 2:
				if ( $total_pm )
				{
					$attach_ids = array();
					$sql = 'SELECT a.attach_id
								FROM ' . ATT_LINKS_TABLE . ' a
									LEFT JOIN ' . PRIVMSGS_TABLE . ' p
										ON p.privmsgs_id = a.privmsgs_id
								WHERE a.privmsgs_id <> 0
									AND p.privmsgs_id IS NULL
								LIMIT ' . ROWS_A_TURN;
					$result = $db->sql_query($sql, false, __LINE__, __FILE__);
					if ( $todo = $db->sql_numrows($result) )
					{
						while ( $row = $db->sql_fetchrow($result) )
						{
							$attach_ids[] = intval($row['attach_id']);
						}
					}
					$db->sql_freeresult($result);

					if ( $count_attach_ids = count($attach_ids) )
					{
						$sql = 'DELETE FROM ' . ATT_LINKS_TABLE . '
									WHERE attach_id IN(' . implode(', ', $attach_ids) . ')';
						$db->sql_query($sql, false, __LINE__, __FILE__);
					}
					unset($attach_ids);

					// count
					$done_pm = $todo ? min($total_pm, $done_pm + $todo) : $total_pm;

					// this step over ?
					if ( $done_pm >= $total_pm )
					{
						$step++;
					}
					break;
				}
				else
				{
					$step++;
				}
		}

		// send a progress bar
		$defs = array(
			'Delete_dead' => array('done' => 'done', 'total' => 'total'),
			'Delete_dead_pm' => array('done' => 'done_pm', 'total' => 'total_pm'),
		);
		$parms = array(
			'step' => $step,
			'total' => $total,
			'done' => $done,
			'total_pm' => $total_pm,
			'done_pm' => $done_pm,
		);
		$this->progress_bar('Delete_dead', $defs, $parms);
	}

	function prune_orphan()
	{
		global $db, $config;

		// process step
		$step = _read('step', TYPE_INT);

		// attachments without attachment desc
		$total = _read('total', TYPE_INT);
		$done = _read('done', TYPE_INT);

		// attachments desc without attachment
		$total_desc = _read('total_desc', TYPE_INT);
		$done_desc = _read('done_desc', TYPE_INT);

		switch ( $step )
		{
			// get totals
			case 0:
				// get total of attachments without attachment desc
				$sql = 'SELECT DISTINCT a.attach_id
							FROM ' . ATT_LINKS_TABLE . ' a
								LEFT JOIN ' . ATT_DESC_TABLE . ' d
									ON d.attach_id = a.attach_id
							WHERE d.attach_id IS NULL';
				$result = $db->sql_query($sql, false, __LINE__, __FILE__);
				$total = $db->sql_numrows($result);
				$db->sql_freeresult($result);
				$done = 0;

				// get total of attachment desc without attachment
				// note there is a low chance a user is currently inputing a post, and we are going to delete
				// his temp attachmens. Well, I think we can go with this and let the user reupload them ;).
				$sql = 'SELECT d.attach_id, d.physical_filename, d.thumbnail
							FROM ' . ATT_DESC_TABLE . ' d
								LEFT JOIN ' . ATT_LINKS_TABLE . ' a
									ON a.attach_id = d.attach_id
							WHERE a.attach_id IS NULL';
				$result = $db->sql_query($sql, false, __LINE__, __FILE__);
				$total_desc = $db->sql_numrows($result);
				$db->sql_freeresult($result);
				$done_desc = 0;

				$step++;
				break;

			// attachments without attachment desc
			case 1:
				if ( $total )
				{
					$attach_ids = array();
					$sql = 'SELECT DISTINCT a.attach_id
								FROM ' . ATT_LINKS_TABLE . ' a
									LEFT JOIN ' . ATT_DESC_TABLE . ' d
										ON d.attach_id = a.attach_id
								WHERE d.attach_id IS NULL
								LIMIT ' . ROWS_A_TURN;
					$result = $db->sql_query($sql, false, __LINE__, __FILE__);
					if ( $todo = $db->sql_numrows($result) )
					{
						while ( $row = $db->sql_fetchrow($result) )
						{
							$attach_ids[] = intval($row['attach_id']);
						}
					}
					$db->sql_freeresult($result);

					if ( $count_attach_ids = count($attach_ids) )
					{
						$sql = 'DELETE FROM ' . ATT_LINKS_TABLE . '
									WHERE attach_id IN(' . implode(', ', $attach_ids) . ')';
						$db->sql_query($sql, false, __LINE__, __FILE__);
					}
					unset($attach_ids);

					// count
					$done = $todo ? min($total, $done + $todo) : $total;

					// this step over ?
					if ( $done >= $total )
					{
						$step++;
					}
					break;
				}
				else
				{
					$step++;
				}

			// attachment desc without attachment
			case 2:
				if ( $total_desc )
				{
					$attach_ids = array();
					$attach_list = array();
					$sql = 'SELECT d.attach_id, d.physical_filename, d.thumbnail
								FROM ' . ATT_DESC_TABLE . ' d
									LEFT JOIN ' . ATT_LINKS_TABLE . ' a
										ON a.attach_id = d.attach_id
								WHERE a.attach_id IS NULL
								LIMIT ' . ROWS_A_TURN;
					$result = $db->sql_query($sql, false, __LINE__, __FILE__);
					if ( $todo = $db->sql_numrows($result) )
					{
						while ( $row = $db->sql_fetchrow($result) )
						{
							$attach_list[ intval($row['attach_id']) ] = array('physical_filename' => $row['physical_filename'], 'thumbnail' => intval($row['thumbnail']));
						}
					}
					$db->sql_freeresult($result);

					if ( !empty($attach_list) )
					{
						$this->handler->open_channel();
						foreach ( $attach_list as $attach_id => $attach )
						{
							$this->handler->unlink($attach['filename'], $attach['thumbnail']);
						}
						$this->handler->close_channel();
						$attach_ids = array_keys($attach_list);
					}
					unset($attach_list);

					// delete description
					if ( !empty($attach_ids) )
					{
						$sql = 'DELETE FROM ' . ATT_DESC_TABLE . '
									WHERE attach_id IN(' . implode(', ', array_keys($attach_ids)) . ')';
						$db->sql_query($sql, false, __LINE__, __FILE__);
					}
					unset($attach_ids);

					// count
					$done_desc = $todo ? min($total_desc, $done_desc + $todo) : $total_desc;

					// this step over ?
					if ( $done_desc >= $total_desc )
					{
						$step++;
					}
					break;
				}
				else
				{
					$step++;
				}
		}

		// send a progress bar
		$defs = array(
			'Delete_orphan' => array('done' => 'done', 'total' => 'total'),
			'Delete_orphan_desc' => array('done' => 'done_desc', 'total' => 'total_desc'),
		);
		$parms = array(
			'step' => $step,
			'total' => $total,
			'done' => $done,
			'total_desc' => $total_desc,
			'done_desc' => $done_desc,
		);
		$this->progress_bar('Delete_orphan', $defs, $parms);
	}

	function resync_posts()
	{
		global $db, $config;

		// process step
		$step = _read('step', TYPE_INT);

		// posts with missing attachments
		$total = _read('total', TYPE_INT);
		$done = _read('done', TYPE_INT);

		// posts missing flag
		$total_pflag = _read('total_pflag', TYPE_INT);
		$done_pflag = _read('done_pflag', TYPE_INT);

		// pms with missing attachments
		$total_pm = _read('total_pm', TYPE_INT);
		$done_pm = _read('done_pm', TYPE_INT);

		// pms missing flag
		$total_pmflag = _read('total_pmflag', TYPE_INT);
		$done_pmflag = _read('done_pmflag', TYPE_INT);

		switch ( $step )
		{
			// get totals
			case 0:
				// get total of posts with missing attachments
				$sql = 'SELECT p.post_id
							FROM ' . POSTS_TABLE . ' p
								LEFT JOIN ' . ATT_LINKS_TABLE . ' a
									ON a.post_id = p.post_id
							WHERE p.post_attachment = 1
								AND a.attach_id IS NULL';
				$result = $db->sql_query($sql, false, __LINE__, __FILE__);
				$total = $db->sql_numrows($result);
				$db->sql_freeresult($result);
				$done = 0;

				// get total of posts with missing attachment flag
				$sql = 'SELECT DISTINCT a.post_id
							FROM ' . ATT_LINKS_TABLE . ' a, ' . POSTS_TABLE . ' p
							WHERE p.post_attachment = 0
								AND a.post_id = p.post_id';
				$result = $db->sql_query($sql, false, __LINE__, __FILE__);
				$total_pflag = $db->sql_numrows($result);
				$db->sql_freeresult($result);
				$done_pflag = 0;

				// get total of private message with missing attachments
				$sql = 'SELECT p.privmsgs_id
							FROM ' . PRIVMSGS_TABLE . ' p
								LEFT JOIN ' . ATT_LINKS_TABLE . ' a
									ON a.privmsgs_id = p.privmsgs_id
							WHERE p.privmsgs_attachment = 1
								AND a.attach_id IS NULL';
				$result = $db->sql_query($sql, false, __LINE__, __FILE__);
				$total_pm = $db->sql_numrows($result);
				$db->sql_freeresult($result);
				$done_pm = 0;

				// get total of pms with missing attachment flag
				$sql = 'SELECT DISTINCT a.privmsgs_id
							FROM ' . ATT_LINKS_TABLE . ' a, ' . PRIVMSGS_TABLE . ' p
							WHERE p.privmsgs_attachment = 0
								AND a.privmsgs_id = p.privmsgs_id';
				$result = $db->sql_query($sql, false, __LINE__, __FILE__);
				$total_pmflag = $db->sql_numrows($result);
				$db->sql_freeresult($result);
				$done_pmflag = 0;

				$step++;
				break;

			// posts with missing attachments
			case 1:
				if ( $total )
				{
					$post_ids = array();
					$sql = 'SELECT p.post_id
								FROM ' . POSTS_TABLE . ' p
									LEFT JOIN ' . ATT_LINKS_TABLE . ' a
										ON a.post_id = p.post_id
								WHERE p.post_attachment = 1
									AND a.attach_id IS NULL
								LIMIT ' . ROWS_A_TURN;
					$result = $db->sql_query($sql, false, __LINE__, __FILE__);
					if ( $todo = $db->sql_numrows($result) )
					{
						while ( $row = $db->sql_fetchrow($result) )
						{
							$post_ids[] = intval($row['post_id']);
						}
					}
					$db->sql_freeresult($result);

					if ( $count_post_ids = count($post_ids) )
					{
						$sql = 'UPDATE ' . POSTS_TABLE . '
									SET post_attachment = 0
									WHERE post_id IN(' . implode(', ', $post_ids) . ')';
						$db->sql_query($sql, false, __LINE__, __FILE__);
					}
					unset($post_ids);

					// count
					$done = $todo ? min($total, $done + $todo) : $total;

					// this step over ?
					if ( $done >= $total )
					{
						$step++;
					}
					break;
				}
				else
				{
					$step++;
				}

			// posts with missing attachment flag
			case 2:
				if ( $total_pflag )
				{
					$post_ids = array();
					$sql = 'SELECT DISTINCT a.post_id
								FROM ' . ATT_LINKS_TABLE . ' a, ' . POSTS_TABLE . ' p
								WHERE p.post_attachment = 0
									AND a.post_id = p.post_id
								LIMIT ' . ROWS_A_TURN;
					$result = $db->sql_query($sql, false, __LINE__, __FILE__);
					if ( $todo = $db->sql_numrows($result) )
					{
						while ( $row = $db->sql_fetchrow($result) )
						{
							$post_ids[] = intval($row['post_id']);
						}
					}
					$db->sql_freeresult($result);

					if ( $count_post_ids = count($post_ids) )
					{
						$sql = 'UPDATE ' . POSTS_TABLE . '
									SET post_attachment = 1
									WHERE post_id IN(' . implode(', ', $post_ids) . ')';
						$db->sql_query($sql, false, __LINE__, __FILE__);
					}
					unset($post_ids);

					// count
					$done_pflag = $todo ? min($total_pflag, $done_pflag + $todo) : $total_pflag;

					// this step over ?
					if ( $done_pflag >= $total_pflag )
					{
						$step++;
					}
					break;
				}
				else
				{
					$step++;
				}

			// pms with missing attachments
			case 3:
				if ( $total_pm )
				{
					$post_ids = array();
					$sql = 'SELECT p.privmsgs_id
								FROM ' . PRIVMSGS_TABLE . ' p
									LEFT JOIN ' . ATT_LINKS_TABLE . ' a
										ON a.privmsgs_id = p.privmsgs_id
								WHERE p.privmsgs_attachment = 1
									AND a.attach_id IS NULL
								LIMIT ' . ROWS_A_TURN;
					$result = $db->sql_query($sql, false, __LINE__, __FILE__);
					if ( $todo = $db->sql_numrows($result) )
					{
						while ( $row = $db->sql_fetchrow($result) )
						{
							$post_ids[] = intval($row['privmsgs_id']);
						}
					}
					$db->sql_freeresult($result);

					if ( $count_post_ids = count($post_ids) )
					{
						$sql = 'UPDATE ' . PRIVMSGS_TABLE . '
									SET privmsgs_attachment = 0
									WHERE privmsgs_id IN(' . implode(', ', $post_ids) . ')';
						$db->sql_query($sql, false, __LINE__, __FILE__);
					}
					unset($post_ids);

					// count
					$done_pm = $todo ? min($total_pm, $done_pm + $todo) : $total_pm;

					// this step over ?
					if ( $done_pm >= $total_pm )
					{
						$step++;
					}
					break;
				}
				else
				{
					$step++;
				}

			// pms with missing attachment flag
			case 4:
				if ( $total_pmflag )
				{
					$post_ids = array();
					$sql = 'SELECT DISTINCT a.privmsgs_id
								FROM ' . ATT_LINKS_TABLE . ' a, ' . PRIVMSGS_TABLE . ' p
								WHERE p.privmsgs_attachment = 0
									AND a.privmsgs = p.privmsgs_id
								LIMIT ' . ROWS_A_TURN;
					$result = $db->sql_query($sql, false, __LINE__, __FILE__);
					if ( $todo = $db->sql_numrows($result) )
					{
						while ( $row = $db->sql_fetchrow($result) )
						{
							$post_ids[] = intval($row['privmsgs_id']);
						}
					}
					$db->sql_freeresult($result);

					if ( $count_post_ids = count($post_ids) )
					{
						$sql = 'UPDATE ' . PRIVMSGS_TABLE . '
									SET privmsgs_attachment = 1
									WHERE privmsgs_id IN(' . implode(', ', $post_ids) . ')';
						$db->sql_query($sql, false, __LINE__, __FILE__);
					}
					unset($post_ids);

					// count
					$done_pmflag = $todo ? min($total_pmflag, $done_pmflag + $todo) : $total_pmflag;

					// this step over ?
					if ( $done_pmflag >= $total_pmflag )
					{
						$step++;
					}
					break;
				}
				else
				{
					$step++;
				}
		}

		// send a progress bar
		$defs = array(
			'Resync_posts' => array('done' => 'done', 'total' => 'total'),
			'Resync_posts_pflag' => array('done' => 'done_pflag', 'total' => 'total_pflag'),
			'Resync_posts_pm' => array('done' => 'done_pm', 'total' => 'total_pm'),
			'Resync_posts_pmflag' => array('done' => 'done_pmflag', 'total' => 'total_pmflag'),
		);
		$parms = array(
			'step' => $step,
			'total' => $total,
			'done' => $done,
			'total_pflag' => $total_pflag,
			'done_pflag' => $done_pflag,
			'total_pm' => $total_pm,
			'done_pm' => $done_pm,
			'total_pmflag' => $total_pmflag,
			'done_pmflag' => $done_pmflag,
		);
		$this->progress_bar('Resync_posts', $defs, $parms);
	}

	function resync_topics()
	{
		global $db, $config;

		// process step
		$step = _read('step', TYPE_INT);

		// attachments without attachment desc
		$total = _read('total', TYPE_INT);
		$done = _read('done', TYPE_INT);

		// attachments desc without attachment
		$total_flag = _read('total_flag', TYPE_INT);
		$done_flag = _read('done_flag', TYPE_INT);

		switch ( $step )
		{
			// get totals
			case 0:
				// get total of topics flaged without post attachments
				$sql = 'SELECT DISTINCT t.topic_id
							FROM ' . TOPICS_TABLE . ' t 
								LEFT JOIN ' . POSTS_TABLE . ' p
									ON p.topic_id = t.topic_id
										AND p.post_attachment = 1
							WHERE t.topic_attachment = 1
								AND p.post_id IS NULL';
				$result = $db->sql_query($sql, false, __LINE__, __FILE__);
				$total = $db->sql_numrows($result);
				$db->sql_freeresult($result);
				$done = 0;

				// get total of topics not flaged having post attachments
				$sql = 'SELECT DISTINCT t.topic_id
							FROM ' . TOPICS_TABLE . ' t, ' . POSTS_TABLE . ' p
							WHERE p.topic_id = t.topic_id
								AND p.post_attachment = 1
								AND t.topic_attachment = 0';
				$result = $db->sql_query($sql, false, __LINE__, __FILE__);
				$total_flag = $db->sql_numrows($result);
				$db->sql_freeresult($result);
				$done_flag = 0;

				$step++;
				break;

			// topics flaged without post attachments
			case 1:
				if ( $total )
				{
					$topic_ids = array();
					$sql = 'SELECT DISTINCT t.topic_id
								FROM ' . TOPICS_TABLE . ' t 
									LEFT JOIN ' . POSTS_TABLE . ' p
										ON p.topic_id = t.topic_id
											AND p.post_attachment = 1
								WHERE t.topic_attachment = 1
									AND p.post_id IS NULL
								LIMIT ' . ROWS_A_TURN;
					$result = $db->sql_query($sql, false, __LINE__, __FILE__);
					if ( $todo = $db->sql_numrows($result) )
					{
						while ( $row = $db->sql_fetchrow($result) )
						{
							$topic_ids[] = intval($row['topic_id']);
						}
					}
					$db->sql_freeresult($result);

					if ( $count_topic_ids = count($topic_ids) )
					{
						$sql = 'UPDATE ' . TOPICS_TABLE . '
									SET topic_attachment = 0
									WHERE topic_id IN(' . implode(', ', $topic_ids) . ')';
						$db->sql_query($sql, false, __LINE__, __FILE__);
					}
					unset($topic_ids);

					// count
					$done = $todo ? min($total, $done + $todo) : $total;

					// this step over ?
					if ( $done >= $total )
					{
						$step++;
					}
					break;
				}
				else
				{
					$step++;
				}

			// topics not flaged having post attachments
			case 2:
				if ( $total_flag )
				{
					$topic_ids = array();
					$sql = 'SELECT DISTINCT t.topic_id
								FROM ' . TOPICS_TABLE . ' t, ' . POSTS_TABLE . ' p
								WHERE p.topic_id = t.topic_id
									AND p.post_attachment = 1
									AND t.topic_attachment = 0
								LIMIT ' . ROWS_A_TURN;
					$result = $db->sql_query($sql, false, __LINE__, __FILE__);
					if ( $todo = $db->sql_numrows($result) )
					{
						while ( $row = $db->sql_fetchrow($result) )
						{
							$topic_ids[] = intval($row['topic_id']);
						}
					}
					$db->sql_freeresult($result);

					if ( $count_topic_ids = count($topic_ids) )
					{
						$sql = 'UPDATE ' . TOPICS_TABLE . '
									SET topic_attachment = 1
									WHERE topic_id IN(' . implode(', ', $topic_ids) . ')';
						$db->sql_query($sql, false, __LINE__, __FILE__);
					}
					unset($topic_ids);

					// count
					$done_flag = $todo ? min($total_flag, $done_flag + $todo) : $total_flag;

					// this step over ?
					if ( $done_flag >= $total_flag )
					{
						$step++;
					}
					break;
				}
				else
				{
					$step++;
				}
		}

		// send a progress bar
		$defs = array(
			'Resync_topics' => array('done' => 'done', 'total' => 'total'),
			'Resync_topics_flag' => array('done' => 'done_flag', 'total' => 'total_flag'),
		);
		$parms = array(
			'step' => $step,
			'total' => $total,
			'done' => $done,
			'total_flag' => $total_flag,
			'done_flag' => $done_flag,
		);
		$this->progress_bar('Resync_topics', $defs, $parms);
	}
}

// instantiate the form
$parms = array(
	'mode' => $menu_id,
	'sub' => $subm_id == $menu_id ? '' : $subm_id,
	'ctx' => $ctx_id == $subm_id ? '' : $ctx_id,
);
$form = new attach_maintain($cp_requester, $parms);
$form->process();
$template->set_filenames(array('body' => 'cp_generic.tpl'));

?>