<?php
/*-----------------------------------------------------------------------------
    IP Search - A phpBB Add-On
  ----------------------------------------------------------------------------
    acp_ip_search.php
       IP Search ACP Module Script
    File Version: 3.1.2
    Begun: August 25, 2003                 Last Modified: May 29, 2007
  ----------------------------------------------------------------------------
    Copyright 2007 by Jeremy Rogers.  Please read the license.txt included
    with the phpBB Add-On listed above for full license and copyright details.
  ----------------------------------------------------------------------------
    Class and Function Quick Reference
             Names                                 Search Labels
         acp_ip_search................................[class]
            main......................................[main]
            prep......................................[prep]
            ip_output_row.............................[ipout]
            run_query.................................[query]
-----------------------------------------------------------------------------*/

/*	acp_ip_search                                           [class]
       Handles all of the IP searching functions.
	   Originally ported from IP Search for phpBB 2, but modified
	   greatly to increase future expansion.
*/
class acp_ip_search
{
	var $u_action;
	var $search_results	= array();
	var $search_terms	= array();
	var $user_ids		= array();
	var $lookups		= array();
	var $guest_lookups	= array();
	var $headings		= array();
	var $results_found	= FALSE;

/*	main                                                    [main]
       Does all the work of IP Search.

    Arguments:
       None.

    Return:
       None.
*/
	function main($id, $mode)
	{
		global $config, $db, $user, $auth, $template, $cache;
		global $phpbb_root_path, $phpbb_admin_path, $phpEx, $table_prefix;

		$user->add_lang('mods/ipsearch');
		$submit = ( isset($_POST['term']) || isset($_GET['term']) ) ? TRUE : FALSE;
		
		$this->tpl_name		= 'ipsearch_search';
		$this->page_title	= 'ACP_IP_SEARCH';

		$template->assign_vars(array(
			'U_IP_SEARCH' => $this->u_action
		));

		if( !$submit )
		{
			// We're done... quit to print the basic search form.
			return;
		}

		// Doing a search...
		$this->search_results['guests'] = array();
		$search_term = request_var('term', '');
		if ( empty($search_term) || strpos($search_term, '.') === FALSE)
		{
			trigger_error($user->lang['IPSEARCH_ENTER_AN_IP'] . adm_back_link($this->u_action));
		}

		$this->tpl_name		= 'ipsearch_results';
		$this->page_title	= 'IPSEARCH_SEARCH_RESULTS';

		$l_host = $host = $ip_list = '';

		if( strpos($search_term, '*') !== FALSE )
		{
			// We're doing a search with wildcards.
			$this->search_terms = array(str_replace('*', '%', $search_term));
		}
		elseif( preg_match('/([0-9]{1,3}\.){3}[0-9]{1,3}/', $search_term) )
		{
			// Full IP address, yay! Let us try to get the matching host.
			$host = @gethostbyaddr($search_term);
			if( empty($host) || $host == $search_term )
			{
				// Oooo, it failed. Make sure this is an empty string,
				// since PHP handles it differently across versions.
				$host = '';
			}
			$this->search_terms = array($search_term);
		}
		else
		{
			// Searching my a host? Well, okay. IP Fetching time!
			$host				= $search_term;
			$this->search_terms	= @gethostbynamel($host);
			if( !is_array($this->search_terms) )
			{
				// Well, that failed, too.
				trigger_error($user->lang['IPSEARCH_NO_RESOLVE'] . adm_back_link($this->u_action));
			}
		}

		$show_host = $show_ip_list = $no_resolve = FALSE;
		if( !empty($host) && $host != $search_term )
		{
			// They searched for IP and we have a host, so let's show it, too.
			$show_host = TRUE;
			$l_host = sprintf($user->lang['IPSEARCH_HOST'], $host);
		}
		elseif( $host == $search_term )
		{
			// They searched for a host and we resolved this to an IP list.
			// Let's show the resolved IPs.
			$show_ip_list = TRUE;
			$ip_list = sprintf($user->lang['IPSEARCH_IP_LIST'], implode(', ', $this->search_terms));
		}
		elseif( !$show_host && !$show_ip_list )
		{
			$no_resolve = TRUE;
		}

		$template->assign_vars(array(
			'SEARCH_TERM'	=> sprintf($user->lang['IPSEARCH_SEARCHED_FOR'], $search_term),
			'HOST'			=> $l_host,
			'IP_LIST'		=> $ip_list,
			'S_IP_LIST'		=> $show_ip_list,
			'S_HOST'		=> $show_host,
			'S_NO_RESOLVE'	=> $no_resolve,
			'S_AGAIN'		=> TRUE
		));

		/* Search Lookups
			Define the tables and fields we will search for IPs.
			The format is:
		$this->lookups[] = array(
			'cat'		=>	cat name
			'id'		=>	id field,
			'table'		=>	table constant/name,
			'ip'		=>	ip field,
			'no_anon'	=>	true
		);

			Some notes:
			Cat_name is a category name, for internal use in storing results
				related to one area. See the existing entries below for example.
				Do not use the same category in more than one lookup.
			Id field is the field of the table containing the user's id. This
				will be selected and used to look up the user's details in the
				users table later.
			IP field is the field of the table containing IP addresses. This
				field will be searched.
			Table constant/name is the name of the table to be searched.
				Generally is should be a constant defined in constants.php.
			The no_anon line is optional. If included, it will cause the search
				to exclude anonymous users in that table. This is only necessary
				on tables that may hold user and guest ips in the same fields.
				Searches for anonymous users should be done separately.
				If in doubt, go ahead and include the no_anon line.
		*/
		// If you would like to add a search on a table, do it here.


		// Default Lookups
		$this->lookups[] = array(
			'cat'		=>	'posts',
			'id'		=>	'poster_id',
			'table'		=>	POSTS_TABLE,
			'ip'		=>	'poster_ip',
			'no_anon'	=>	true
		);

		$this->lookups[] = array(
			'cat'		=>	'pms',
			'id'		=>	'author_id',
			'table'		=>	PRIVMSGS_TABLE,
			'ip'		=>	'author_ip'
		);

		$this->lookups[] = array(
			'cat'		=>	'sessions',
			'id'		=>	'session_user_id',
			'table'		=>	SESSIONS_TABLE,
			'ip'		=>	'session_ip',
			'no_anon'	=>	true
		);

		$this->lookups[] = array(
			'cat'		=>	'keys',
			'id'		=>	'user_id',
			'table'		=>	SESSIONS_KEYS_TABLE,
			'ip'		=>	'last_ip',
			'no_anon'	=>	true
		);

		$this->lookups[] = array(
			'cat'		=>	'votes',
			'id'		=>	'vote_user_id',
			'table'		=>	POLL_VOTES_TABLE,
			'ip'		=>	'vote_user_ip',
			'no_anon'	=>	true
		);

		$this->lookups[] = array(
			'cat'		=>	'logs',
			'id'		=>	'user_id',
			'table'		=>	LOG_TABLE,
			'ip'		=>	'log_ip',
			'no_anon'	=>	true
		);

		$this->lookups[] = array(
			'cat'		=>	'users',
			'id'		=>	'user_id',
			'table'		=>	USERS_TABLE,
			'ip'		=>	'user_ip',
			'no_anon'	=>	true
		);

		foreach($this->lookups as $v)
		{
			$this->run_query($v);
		}

		// All of that was for registered users. Now let's get the guests.
		// The format is mostly the same. The differences are:
		//			We use $this->guest_lookups
		//			We may pull details other than the users' id number
		//			no_anon has no use and is not needed.
		// Be sure to use the same cat names used in the registered user
		// searches.
		$this->guest_lookups[] = array(
			'cat'		=>	'posts',
			'id'		=>	'post_username',
			'table'		=>	POSTS_TABLE,
			'ip'		=>	'poster_ip'
		);

		$this->guest_lookups[] = array(
			'cat'		=>	'sessions',
			'id'		=>	'session_user_id',
			'table'		=>	SESSIONS_TABLE,
			'ip'		=>	'session_ip'
		);

		$this->guest_lookups[] = array(
			'cat'		=>	'votes',
			'id'		=>	'vote_user_id',
			'table'		=>	POLL_VOTES_TABLE,
			'ip'		=>	'vote_user_ip'
		);

		$this->guest_lookups[] = array(
			'cat'		=>	'logs',
			'id'		=>	'user_id',
			'table'		=>	LOG_TABLE,
			'ip'		=>	'log_ip'
		);

		foreach($this->guest_lookups as $v)
		{
			$this->run_query($v, true);
		}

		// Now finished with IP lookups. It is time to get usernames.
		// First, clear out duplicate user ids.
		if( !$this->results_found )
		{
			$message = $user->lang['IP_SEARCH_NADA'] . '<br /><br />';
			$message .= sprintf($user->lang['IPSEARCH_SEARCHED_FOR'], $search_term);
			if( $show_host )
			{
				$message .= '<br /><br />' . $l_host;
			}
			elseif( $show_ip_list )
			{
				$message .= '<br /><br />' . $ip_list;
			}
			elseif( $no_resolve )
			{
				$message .= '<br /><br />' . $user->lang['IPSEARCH_HOST_NOT_FOUND'];
			}
			trigger_error($message . adm_back_link($this->u_action));
		}

		$this->user_ids = array_unique($this->user_ids);
		$result = $db->sql_query('SELECT user_id, username FROM ' . USERS_TABLE . '
					WHERE ' . $db->sql_in_set('user_id', $this->user_ids));
		$users = $db->sql_fetchrowset($result);
		$db->sql_freeresult($result);

		// Print users
		foreach($users as $v)
		{
			$this->ip_output_row($v);
		}

		// Print guests
		$this->ip_output_row(array('user_id' => ANONYMOUS, 'username' => $user->lang['GUEST']));
	}

/*	prep                                                    [prep]
       Prepares an array to hold the number of results found by
	   an IP Search.

    Arguments:
       None.

    Return:
       array  $base  An array, with each value defaulted to 0.
*/
	function prep()
	{
		$base = array();
		foreach($this->lookups as $k=>$v)
		{
			$base[$v['cat']] = 0;
		}
		return $base;
	}

/*	ip_output_row                                           [ipout]
	   Prints out a row of IP results.

	Arguments:
	   $user_info - An array of user details, consisting of at least the
						user_id and username.

	Return:
	   None.
																		*/
	function ip_output_row($user_info)
	{
		global $lang, $theme, $phpbb_root_path, $phpEx, $user, $template, $auth;

		$user_id = intval($user_info['user_id']);

		if( $user_id == ANONYMOUS )
		{
			$base_results =& $this->search_results['guests'];
		}
		else
		{
			$base_results =& $this->search_results;
		}

		if( empty($base_results) )
		{
			return;
		}

		$user_results = array();
		$totals = $this->prep();
		// Run through all results found and extract the ones related to this
		// specific user.
		foreach($base_results as $k=>$v)
		{
			if( $k == 'guests' )
			{
				continue;
			}

			foreach($v as $result)
			{
				if( $user_id != ANONYMOUS && $result['user_id'] != $user_id )
				{
					continue;
				}

				if( !isset($user_results[$result['recorded_ip']]) )
				{
					$user_results[$result['recorded_ip']] = $this->prep();
				}

				$user_results[$result['recorded_ip']][$k] = $result['total'];
				$totals[$k] += $result['total'];
			}
		}

		// Users/Guests results header
		$heading = ( $user_id != ANONYMOUS ) ? $user->lang['IPSEARCH_USERS'] : $user->lang['G_GUESTS'];
		$show_header = FALSE;
		if( !isset($this->headings[$heading]) )
		{
			$show_header = $this->headings[$heading] = true;
		}

		if( $auth->acl_get('a_user') )
		{
			$user_link = '<a href="' . append_sid("{$phpbb_root_path}adm/index.$phpEx", 'i=users&amp;mode=overview&amp;u=' . $user_id, true, $user->session_id) . '">' . $user_info['username'] . '</a>';
		}
		else
		{
			$user_link = $user_info['username'];
		}

		if( count($user_results) > 1 )
		{
			// More than one IP was found for the user.
			// We need to print out the totals row.
			$tpl_vars = array(
				'USERNAME'			=>	$user_link,
				'SHOW_HEADER'		=>	$show_header,
				'SHOW_TOTAL'		=>	TRUE,
				'HEADER'			=>	$heading,
				'IP'				=>	$lang['IPSEARCH_TOTALS'],
			);

			foreach($totals as $cat=>$num)
			{
				$tpl_vars[strtoupper($cat) . '_COUNT'] = $num;
			}
			$template->assign_block_vars('users', $tpl_vars);
			$show_header = FALSE;
		}

		foreach($user_results as $k=>$v)
		{
			$tpl_vars = array(
				'USERNAME'			=>	$user_link,
				'SHOW_HEADER'		=>	$show_header,
				'SHOW_TOTAL'		=>	FALSE,
				'HEADER'			=>	$heading,
				'IP'				=>	$k,
			);
			foreach($v as $cat=>$num)
			{
				$tpl_vars[strtoupper($cat) . '_COUNT'] = $num;
			}
			$template->assign_block_vars('users', $tpl_vars);
			$show_header = FALSE;
		}
	}

/*	run_query                                               [query]
       Builds and executes a query to search the database for records related
	   to one or more IP addresses.

    Arguments:
       $details	- Details of a search to be performed.  These are defined above
					as $this->lookups, etc.
       $guests	- Searching for guest users only? TRUE or FALSE.

    Return:
       None.
*/
	function run_query($details, $guests = FALSE)
	{
		global $db;

		$no_anon = $error = FALSE;
		$msg = '';
		$cat = $details['cat'];
		unset($details['cat']);
		if( $guests )
		{
			if( isset($this->search_results['guests'][$cat]) )
			{
				$msg = sprintf($user->lang['IPSEARCH_BAD_CONFIG_GUESTS'], $cat);
			}
		}
		else
		{
			if( isset($this->search_results[$cat]) )
			{
				$msg = sprintf($user->lang['IPSEARCH_BAD_CONFIG'], $cat);
			}
		}

		if( $error )
		{
			trigger_error($msg . adm_back_link($this->u_action));
		}

		$base_query = 'SELECT %1$s as user_id, %3$s as recorded_ip, COUNT(*) as total 
						FROM %2$s
						WHERE %4$s
						GROUP BY %3$s, %1$s';
		if( isset($details['no_anon']) )
		{
			$no_anon = array_pop($details);
		}
		$details['where'] = $db->sql_in_set($details['ip'], $this->search_terms);
		if( strpos($details['where'], '%') !== FALSE )
		{
			$details['where'] = str_replace('=', 'LIKE', $details['where']);
		}
		if( $no_anon )
		{
			$details['where'] = $details['id'] . ' <> ' . ANONYMOUS . ' AND ' .  $details['where'];
		}
		elseif( $guests )
		{
			$details['where'] = $details['id'] . ' = ' . ANONYMOUS . ' AND ' .  $details['where'];
		}
		
		$result = $db->sql_query(vsprintf($base_query, $details));
		while( $row = $db->sql_fetchrow($result) )
		{
			if( !$guests )
			{
				$this->user_ids[] = $row['user_id'];
				$this->search_results[$cat][] = $row;
				$this->results_found = TRUE;
			}
			else
			{
				$this->search_results['guests'][$cat][] = $row;
				$this->results_found = TRUE;
			}
		}
		$db->sql_freeresult($result);
	}
}
?>